SwiftUI Advanced Handbook #1-28 PDF
SwiftUI Advanced Handbook #1-28 PDF
1 Firebase Auth 3
2 Read from Firestore 9
3 Write to Firestore 17
4 Join an array of Strings 21
5 Data from JSON 24
6 HTTP request 28
7 WKWebView 38
8 Code highlighting in a WebView 45
9 Test for production in the Simulator 48
10 Debug performance in a WebView 50
11 Debug a crash log 52
12 Simulate a bad network 55
13 Archive a build in Xcode 59
14 Apollo GraphQL Part I 62
15 Apollo GraphQL Part 2 71
16 Apollo GraphQL Part 3 77
17 Con guration les in Xcode 80
18 App Review 85
19 ImagePicker 89
20 Compress a UIImage 95
21 Firebase Storage 97
22 Search Feature 105
23 Push noti cations Part 1 116
24 Push noti cations Part 2 120
25 Push noti cations Part 3 128
26 Network connection 134
27 Download les locally Part 1 139
28 Download les locally Part 2 147
© Design+Code
fi
fi
fi
fi
fi
fi
fi
1 Firebase Auth
Firebase is a backend service and is categorized as a NoSQL database program. The platform provides some really great
development tools and services such as a real-time database, authentication and much more.
In this section, we'll learn how to implement Firebase with SwiftUI. Head over to Firebase and login with your Google
account. Let's start off with creating a Firebase project. Additionally, here's the link to a step-by-step guide for installing
Firebase. There is also a SPM guide.
One of the best things about Firebase is that it allows real-time syncing of data. It also integrates well with a number of
apps such as Google Ads and Slack to make the development process more efficient. Features such as Firebase
Authentication makes the sign up/sign in process way much easier.
Firebase
After logging in, click on 'Add project' and name your project. Click on Continue, if you've Google Analytics, you can also
add that to the project. Finally, click on 'Create project'. When it's done, click on Continue and you'll see the dashboard.
Now, click on the settings icon and go to project settings.
© Design+Code
Setting the App
In 'Your apps', select iOS. To get the iOS bundle ID, head over to XCode and then to Project Settings. Copy the Bundle
Identifier and paste it as the input value of iOS bundle ID. Click on 'Register app' and then download the config file in
the next step. Drag and drop the file in XCode, next to Lottie.
To install Firebase using SPM, go to File → Swift Packages → Add Package Dependency... Enter the following URL. Select
Firebase Auth at the end.
Initialize Firebase
To initialize Firebase in our app, we'll need to import it first. At the top, add in an import statement as follows:
© Design+Code
Firebase Console Setup
Let's head back to the Firebase console and finish our setup. Since we're already done with steps 3 and 4, click 'Next'
for both. The next step verifies the installation. For this, go to XCode and run the iOS simulator. After it finishes building
the app, in the Console, you'll see a Congratulations message. This ensures the verification. Now, click on 'Continue to
console' button.
Firebase Authentication
In this step, we'll start using Firebase authentication. In the sidebar, click on Authentication and then on the 'Set up
sign-in method' button.
© Design+Code
In Sign-in providers, select the Email/Password option and toggle the option that says 'Allow users...'. Click the 'Save'
button.
© Design+Code
Creating User
To create a user, click on 'Users' and then 'Add user'. Fill in the email as well as the password and then click on the 'Add
user' button next to 'Cancel'.
Login Screen
Here, we'll add the following code inside the login function.
© Design+Code
Testing
To test whether it works or not, you'll have to run the Simulator. You cannot test Firebase calls in the Preview. Once the
simulator is running, you can enter the test email and test password we added via the Firebase Console
© Design+Code
.
2 Read from Firestore
INSTALL CLOUD FIRESTORE IN YOUR APPLICATION TO FETCH AND READ DATA FROM A COLLECTION
Let's say you are building a restaurants application and are storing the restaurants' data on Firestore. Now, you want to
get that data from Firestore to display it on your application. Keep on reading to learn how to do just that.
Note: If you haven't, I suggest reading the section about setting up Firebase Auth in your application. You must have
Firebase available in your application, as well as a Firestore database in your project, in order to continue.
Setup
Enable Firestore in your application by navigating to the Cloud Firestore tab in Firebase. Then, click on Create database.
A modal will popup and ask if you want to start in production mode or test mode. Choose test mode for now. This will
allow you to test Firestore freely, and you won't be blocked by permission rules. Click on Next.
© Design+Code
Next, you'll need to choose where you want your Firestore server to be located. This cannot be changed afterwards, so
select the one most appropriate to your current location. For me, I will keep nam5 (us-central). Finally, click on Enable.
© Design+Code
FirestoreManager class
After your database is created, you can go to your Xcode project and create a file named FirestoreManager.swift. Import
Firebase at the top of your file.
Let's start by learning how to read a single Firestore document. Let's say only want to extract data from a restaurant
called Pizza Mania (saved as PizzaMania in the Restaurants collection).
In the FirestoreManager class, create a @Published variable called restaurant. It'll be a String.
© Design+Code
In the code above, we are importing the Firestore database.
Then, we are specifying the reference of the document (docRef) we want to fetch data from.
Next, we will get the document with the .getDocument method from Firestore. If there's an error, we'll print the error and
exit the function. If the document exists, we want to call the .data() to get the actual data, then printing on the console
and saving it to the restaurant variable we created. If not, we will print Document does not exist in the console.
© Design+Code
Loop through a collection
If you want to loop through the entire restaurant collection to get each document, the code will look a little different:
In the code above, instead of getting a specific document, we will get all documents with .getDocuments(). Then, we are
looping through each document in the querySnapshot's documents and printing it on the console.
Add environmentObjects
© Design+Code
Remember to add the environmentObject to your preview as well to make it work:
© Design+Code
Display the data
Once you're ready to release your app, remember to update your Firestore rules to protect your database and not allow
anyone to read and write your database.
Navigate to Cloud Firestore and click on the Rules tab. There, you'll see the rules that are currently applied to your
database. If you started in test mode, anyone will be able to read and write to your database for about a month after you
created the database.
You want update the rules depending on how your users will interact with your database. Are they going to only read data?
Will they also be able to write data?
In this case, our users are only allowed read the Restaurants collection from our database, so we will update the rules like
so:
© Design+Code
You can read more about Cloud Firestore Rules here.
Cloud Firestore Rules comes with a handy tool called Rules Playground, where you can test your rules before publishing
them. Here, my simulated read passed, so I'll click on Publish and we're good to go!
© Design+Code
3 Write to Firestore
Let's continue with our restaurant application from the Read from Firestore section. Now, we want to add or update
documents in Firestore. If you haven't, I suggest reading the sections about setting up Firebase Auth and Read from
Firestore. You must have Firebase available in your application, as well as a Firestore database in your project, in order to
continue.
FirestoreManager class
Create a FirestoreManager class. Learn more about it in the Read from Firestore section of this SwiftUI Handbook.
Create a document
Let's say we want to create a new restaurant called Poutine Fiesta in our database. We will need to create a new
document.
© Design+Code
In this function, we are passing a restaurantName as an argument. Inside of the function, we are creating a database
reference with Firestore.firestore(). Next, we specify our docRef. In my case, I want to save the new document under
the Restaurants collection.
If you want to add multiple fields to the document, I suggest you create a dictionary variable (as [String: Any]) with all
your data first, then pass it in the .setData method.
Note that when using the .setData(), it will overwrite the existing data in the document, unless you set the merge option
to true (see below).
Update a document
Let's say that Poutine Fiesta has moved to a new address and we want to update it.
© Design+Code
However, I prefer to use the .setData() method and set the merge option to true, as it will update the document, and also
create it if it doesn't exist. The downside with .updateData() is that it will run into an error if the document doesn't exist.
And voilà! Now you can create new documents and update them in Firestore!
You can read more about adding and updating data in Firestore's documentation.
Once you're ready to release your app, remember to update your Firestore rules to protect your database and not allow
anyone to read and write your database.
© Design+Code
Navigate to Cloud Firestore and click on the Rules tab. There, you'll see the rules that are currently applied to your
database.
We added the feature to write to our database, so we will need to add the write keyword.
© Design+Code
.
4 Join an array of Strings
Create a function
Firstly, to test your function, it is very useful to use Xcode's Playground. Learn how to use Playground by reading the
section Xcode Playground in this handbook.
Then, create a function to transform your array into a serialized string and test it in your Playground.
After clicking on Play in the Playground and making sure that everything works as expected, we can use this function in
our project.
© Design+Code
Turn it into an extension
If you plan to use this function at many places in the project, it would be good to turn it into an extension. An extension
lets you add a functionality to an existing class, structure, enumeration or protocol in Swift, and also makes your code way
cleaner.
In our case, we want to turn the serialized function into a reusable extension over the project. Therefore, we will can
create a new file called Extensions.swift and add the following extension to the BidirectionalCollection protocol :
With the above code, we can simply call .serialized wherever in our project on a String collection (or a
BidirectionalCollection protocol), and we will get the serialized String:
© Design+Code
© Design+Code
5 Data from JSON
Data is the core of any application. If you are not loading data from a third-party service like Contentful, you can rely on
local data from a JSON file. Let's learn how to load and parse the JSON data into our SwiftUI application.
What is JSON?
JSON, also known as JavaScript Object Notation, is a lightweight format of data. It works in key-value pairs and only
accepts certain types of values: object, array, string, number, boolean, null. Here is a simple example of what JSON
looks like:
Let's start by creating our model in our application. As an example, we'll use an Animal model. Each animal will have an
id, a name, an age and a type.
Then, create your JSON file following the model you just created. Since we want to display multiple animals in our
application, our JSON will be an array of Animals.
© Design+Code
Note: Don't forget to validate your JSON data, as you can get errors while decoding the data. You can use JSONLint to
check if your JSON is valid.
Next, let's create our ModelData where we'll load and parse the JSON data into our app.
© Design+Code
The load() function above with take the file from the file path you passed as an argument and decode the JSON data into
a format that Swift can read. If there's an error, the error will be printed in the console.
After the load function is completed, your data will be available and ready to use by simply calling the animals variable.
It's important to specify the type of data you want to save in the animals variable, as the load() function needs to know
what type of data it should output.
You just need to call the animals variable and iterate over each animal to display them in your View.
© Design+Code
© Design+Code
6 HTTP request
When creating an application, you usually need to make an HTTP request to get some data from an API. In SwiftUI, to do
so, you could use a package manager (like Swift-Request or Alamofire), but it's quite easy to create your own HTTP
request function. Let's see how to do just that. For this tutorial, we will use JSONPlaceholder, a free REST API. We will call
the /users endpoint in order to get a list of 10 users and display them in our View.
Network class
We'll need to create a class that conforms to the ObservableObject protocol. By conforming our class to an
ObservableObject, the changes in the class will automatically be reflected in our View. Let's create a Network.swift file,
in which we will call the API.
User model
Next, we'll create a User model. By doing so, we can conform our variables to a User data type. The model will also allow
us to decode the JSON we will get from the API into a User data type. Moreover, we'll be able to call the user's name by
simply writing user.name.
First, we'll need to know how the JSON data we get is structured. We can head over to https://
jsonplaceholder.typicode.com/users to see how it is structured:
© Design+Code
As you can see in the image above, the JSON is an array of objects. Each object represents a user, and it contains many
key-value pairs.
Back in our project, we'll need to create a struct that contains all of these key-value pairs. Luckily, I already created the
User model for you, below. Simply create a new User.swift file, and paste the following code:
© Design+Code
This model conforms to the Identifiable and Decodable protocols. Identifiable means that each item has a unique ID.
Decodable means that it can be decoded - for example, we can transform a JSON object into this data model.
@Published variable
Back in Network.swift, we'll need to create a @Published users variable inside of the class. The variable's type will be an
array of Users. We'll initialize the variable with an empty array to begin with.
Get request
Now, we need to create our getUsers function to fetch the users from the API. Create the function inside of the Network
class.
© Design+Code
In the code above, we are making sure that we have a URL before running the next line of code. With this URL, we are
creating a URLRequest, and passing it to our dataTask.
We are making sure that there's no error and that we do get a response back. If the response is 200 Okay, we double
check that we have data.
We then decode the data we get, which is JSON format, using JSONDecoder, and decode the data into an array of
Users. Once the decoding is done, we assign it to the users variable we defined at the top of the class.
© Design+Code
Finally, we are resuming our dataTask with dataTask.resume().
Create an environmentObject
Now that our function is created, we need to add our Network class as an EnvironmentObject in the
ProjectNameApp.swift file.
To make the preview work, remember to add the environmentObject in your ContentView's preview as well, as the
preview is a different entity from the App:
© Design+Code
Call getUsers
In your body, create a ScrollView and onAppear of it, we'll call our getUsers function:
Then, simply iterate over network.users. We'll display each user's ID, name, email and phone.
© Design+Code
If you're using the Preview, remember to press play to fetch the data from the API. The results should instantly appear if
you're using the Simulator.
Final code
© Design+Code
© Design+Code
This is the final code for ContentView:
© Design+Code
This is the final result in the Simulator:
© Design+Code
7 WKWebView
INTEGRATE AN HTML PAGE INTO YOUR SWIFTUI APPLICATION USING WKWEBVIEW AND BY CONVERTING MARKDOWN INTO HTML
Sometimes, you'll fetch content from a Content Management System, like Contentful, in the format of Markdown code,
and you'll want to display it in your application. To do so, you'll need to convert the Markdown into HTML code and
display it in a WebView.
Markdown code
Markdown is a lightweight format to style any text and is really easy to use. For example, all the READMEs on Github are
written in Markdown. You can learn more about Markdown's syntax here.
To start off, we'll need to get our Markdown text. We will use the following sample Markdown text from Github Guides and
we will convert it into HTML code.
© Design+Code
© Design+Code
Convert Markdown into HTML
To convert the Markdown text into HTML, we will need to use a package called Ink, which is a package written in Swift that
does just that. Let's add the package using Swift Package Manager and the Github repository link (https://github.com/
JohnSundell/Ink).
Next, let's create a new file called ParseContent.swift. Let's import Ink at the top of our file first.
In that class, let's save our Markdown text into a multiline String
© Design+Code
.
© Design+Code
Then, let's use the Ink package to convert the Markdown into HTML.
Create a new file called WebView.swift that will take the following WebView struct:
Afterwards, you'll just need to call the WebView struct and pass in your html String as an argument, and you'll get your
HTML page.
© Design+Code
Add some styling to your WebView
By default, the HTML rendered in the WebView will be in Times New Roman. In order to style it, let's go back to our
ParseContent.swift file. There, we will use inline CSS in order to add some styling to our WebView.
© Design+Code
In the above code, we are adding a padding on the div and changing the font-family to the default Apple system font,
which is SF Pro.
You can style your HTML as much as you want and also style a specific tag, like the h1, by using regex expressions and
replacing all the occurrences, like so:
© Design+Code
8 Code highlighting in a WebView
USE HIGHLIGHT.JS TO CONVERT YOUR CODE BLOCKS INTO BEAUTIFUL HIGHLIGHTED CODE IN A WEBVIEW
If you wish to add code highlighting to your application in a WebView, there's a lot of packages that can help you achieve
that. You could try to use packages built just for Swift, like Highlightr or Splash, but it can add complexity to your code
and affect the performance of your View. As a WebView is basically an HTML page, you can simply use a Javascript
package to do just that. For example, there's Prism or Highlight.js that are two of the most popular syntax highlighting
packages out there. In this tutorial, we'll learn about Highlight.js.
What is Highlight.js?
Highlight.js is a Javascript package that supports code highlighting for many languages, including Javascript, CSS,
Typescript and Swift. It also comes in with over 95 styles for you to choose from.
Make sure to have a WebView in your application. Read the section about WKWebView in this handbook to learn how to
integrate an HTML page into your SwiftUI application.
Then, your markdown text should include a code block, like this one:
© Design+Code
In your parse function, wrap your HTML content with a head and a body.
Between the HEAD tags, we will add the following link, which will link our webView to Highlight.js's stylesheet, hosted on a
Content Delivery Network (or CDN), and will enable the style for the code highlighting.
It's easy to change to another theme, by simply changing the default into the style you want:
Add the following lines of script just before the BODY closing tag. This will connect to the Highlight.js script hosted on a
CDN, as well as initialize the function.
You can style your code block by finding all occurrences of the code tag in your HTML, then adding some style with the
style attribute. Here is an example in my parse function:
© Design+Code
fThe final result will look like this:
© Design+Code
9 Test for production in the Simulator
Test your application in production mode to make sure that everything works as planned. When you build your application
in Xcode, it's set by default in Debug mode. You can test for Release on the Simulator by easily editing your scheme.
Then, you'll need to go to the bottom of the list, where you'll select the Edit Scheme... option.
© Design+Code
A window will open up and under Build Configuration, you'll be able to change it to Release. You can now build your app
for Release and test your app in production mode.
© Design+Code
10 Debug performance in a WebView
Debugging the performance of a WebView can be quite tricky, because you don't know if it's the WebView that's affecting
the CPU usage, or the SwiftUI code surrounding it. Apple created a lot of tools to help developers debug the performance
issue in a WebView. Let's learn how to enable Web Inspector to debug the performance issues of your app.
If you don't see the Develop menu, click on Safari > Preferences > Advanced > Show Develop Menu in menu bar. A
shortcut to access the Preferences menu in Safari is Command (⌘) + , .
You can also do the same on your device by going into Settings > Safari > Advanced > Enable Web Inspector.
Open the device or the Simulator on which you want to debug the performance issues.
Go back to Safari and click on Develop in the top menu. There, you'll see all the available devices. Choose the one you're
working with and select the WebView you wish to debug.
© Design+Code
Track your performance
The Web Inspector will open and you'll be able to record the performance of your WebView. All the events happening
while you were using your app will be recorded, as well as all the frames per seconds rendered.
To learn more about Web Inspector, you can read Apple's documentation
© Design+Code
.
11 Debug a crash log
LEARN HOW TO DEBUG A CRASH LOG FROM APP STORE CONNECT IN XCODE
After you upload your app to App Store Connect, you can either do a beta testing or upload it to the App Store. Either way,
you can get the number of crashes for each of your builds and access the crash logs directly from Xcode.
In Xcode, all the crash logs are logged under Window > Organizer, and under the Crashes tab.
Each crash log contains all the details you need to debug the crashes users encountered.
In the Organizer, at the top, next to the Crashes word, you can select the specific Version and Build of your app you
wish to debug.
Under this, you can find all the crashes the users encountered while using your app. On the right panel, you'll find the
thread leading to the crash or the bug.
© Design+Code
Going further
Sometimes, the Thread does not give enough information to help you debug. Therefore, you can dig deeper into the bug
and read the raw Crash log. To do so, Control (^) + Click, then Show in Finder on a crash.
It will open up all the Crash reports in Finder. On the selected crash report, Control (^) + Click again, and click on Show
Package Contents.
© Design+Code
Then, navigate into DistributionInfos > all > Logs and open the crash report you want to investigate. There, you'll be able
to dig deeper into the crash and what caused it
© Design+Code
.
12 Simulate a bad network
TEST YOUR SWIFTUI APPLICATION BY SIMULATING A BAD NETWORK CONNECTION WITH NETWORK LINK CONDITIONNER
While developing your application, you will want to simulate a bad network connection to see what some of your users
might experience on a bad network connection, and to pinpoint where you have to improve your app's performance.
Apple provided a Developer Tools Package where you can simulate a bad network connection on your Mac.
Note: This affects your whole computer, not just the Simulator.
Go on https://developer.apple.com/download/more/ and search and download the Additional Tools for Xcode for the
Xcode version you have.
Once the package is downloaded on your computer, open the Hardware folder and click on Network Link
Conditioner.prefPane.
© Design+Code
Install Network Link Conditioner on your Mac
You will be asked to install Network Link Conditioner on your Mac. Click Install.
© Design+Code
Simulate the network connection speed
Your System Preferences window will automatically close and open up again, and you'll see at the bottom Network Link
Conditioner. Double click on it and under Profile, you'll be able to select the network speed you want to simulate. Don't
forget to toggle the Network Link Conditioner ON.
Note that a bad network simulation will affect your whole computer, and not just your Simulator. Don't forget to turn it
back off after you finished testing, or your Internet connection will be really slow.
If you run into this message, you already have Network Link Conditioner installed on your Mac. You can either right-click
on Network Link Conditioner in System Preferences and Remove "Network LinkConditioner" Preference Pane to be
able to reinstall it, or simply use the one already installed on your Mac.
© Design+Code
© Design+Code
13 Archive a build in Xcode
Archiving a build in Xcode is useful when you want users to test your beta app, or when you want to release a new version
of your application.
Before beginning to archive your build, you need to make sure that the build is successful not only for Debug, for also for
Release. Remember to test thoroughly with different types of scenarios to make sure there's no crash or bug. See the
Test for production in the Simulator section to learn how to test for production in the Simulator.
To start off, in Xcode, at the top, you need to set your simulator to Any iOS device.
Navigate to your project's settings. Under iOS (or the target you want to build your app for) > Identity, you'll want to
increment the Build number. For example, if the Build number was 1, you'll want to set it to 2.
© Design+Code
Then, in the top menu, under Product, click on Archive.
After the archive is completed, a window will popup and you'll be able to Validate App. Once it's validated, you can
Distribute App and it will upload it to App Store Connect, where you'll be able to share it with Beta testers or upload it
directly to the App Store.
© Design+Code
© Design+Code
14 Apollo GraphQL Part I
Apollo GraphQL is a platform that lets you do API calls using GraphQL. It's the modern way to fetch data, as it deals with
asynchronous fetching and only fetches the data you really need. Therefore, there won't be unnecessary data that takes
up unnecessary space and your application will load faster.
In this three-part tutorial about Apollo GraphQL, we'll learn the complete flow to install Apollo GraphQL, fetch and process
the data, and display the data in our View. In this first section, we'll cover how to install Apollo in our application.
Install Apollo
All the instructions about installation can be found on the Apollo GraphQL documentation page.
Install the Apollo Swift Package by entering the Apollo iOS Github's repository link (https://github.com/apollographql/
apollo-ios.git) in the Add Package Dependency window.
© Design+Code
Once the package has been fetched, check only the Apollo option as you'll only need this package. Then, click Finish.
© Design+Code
Add your schema
In order to run your query, Apollo requires a schema.json file. You can download your schema by downloading the Apollo
CLI (if you don't have it already) and running the following command in your terminal - and replacing the link with your API
link:
Note: This schema file should be added in the folder where most of your code is, but not in the .xcodeproj folder nor
the .xcworkspace folder. It's safe to put it in the Shared folder.
For this tutorial, we will use a sample schema from Apollo. Run the following command in your terminal to download the
schema:
This command will download the schema and save it into the folder you currently are. Don't forget to drag and drop it into
the Shared folder of your Xcode project.
Next, you'll need to create a GraphQL query to fetch data. You can use the schema's playground to test your query before
putting it in your project. Below is a valid query using Apollo's sample schema.
© Design+Code
Note: Apollo requires that each query has a unique name. In this case, this query is named LaunchList.
If your query returns a result in the playground, it's safe to put it in your project. Create a new file and select Empty. Name
it LaunchQuery.graphql and paste the query in there.
© Design+Code
For each target (iOS, macOS), go to the Build Phases tab and add a new Build Phase by clicking on the + sign at the top.
In this tutorial, we'll only focus on the iOS target.
A new Run script will appear. Change its name to Generate Apollo GraphQL API
© Design+Code
Then, drag and drop Generate Apollo GraphQL API just after Dependencies and before Compile Sources. The order of
the scripts should look like this:
Next, copy the following code and add it in the Generate Apollo GraphQL API that you just created.
Note 1: If you are using another Package manager than Swift Package Manager, visit Apollo GraphQL's documentation to
copy and paste the right script.
Note 2: If you put your schema.json file in the Shared folder, change ${TARGET_NAME} to Shared.
© Design+Code
© Design+Code
Then, try to build your application. If the build succeeded, then you're all set and ready to work with the data!
Note: When you build, the API.swift file is generated automatically. So every time you change something in your query, if
API.swift isn't automatically modified, you'll need to add the file to your Xcode project again.
The preview might always pause after adding the Apollo script. To prevent this from happening, check the For install
builds only option, just below the script. Remember to uncheck it every time you add a new query or update a query so
Apollo can re-generate the API.swift file.
© Design+Code
Conclusion
In the next part of this three-part Apollo GraphQL tutorial, we'll learn how to make the network call and process the data
we get from the server into our own data model type. See you in the next section
© Design+Code
!
15 Apollo GraphQL Part 2
MAKE A NETWORK CALL TO FETCH YOUR DATA AND PROCESS IT INTO YOUR OWN DATA TYPE
Apollo GraphQL is a platform that lets you do API calls using GraphQL. It's the modern way to fetch data, as it deals with
asynchronous fetching and only fetches the data you really need. In this second section, we will call the server and fetch
data into our project. Once fetched, we will display the data in our application.
If you haven't, I highly recommend you to follow the first part of the Apollo GraphQL tutorial to learn how to install Apollo
GraphQL into your Xcode project.
Build your project (⌘ + B). Once it's succeeded, an API.swift file will appear in the same folder as your schema.json file
we generated in Apollo GraphQL Part I. If it's not in your Xcode project, remember to drag and drop it from your Finder
into your project or click on File > Add new Files to ProjectName and select the API.swift file.
The API.swift file is auto-generated by Apollo and you should never edit it as it can lead to bugs in your application.
Every time you create a new query or change something in a query, this file will be re-generated automatically.
In a new Network.swift file, paste the following code. This will allow you to connect to the server and fetch data with your
GraphQL query.
© Design+Code
Create your ViewModel
A ViewModel allows you to process your data before displaying it in a View, and makes your code cleaner as it's easier to
read when you separate the functions from the UI. Create a LaunchViewModel.swift file where we'll put the function to
fetch our data.
Next, initialize your LaunchViewModel as a StateObject in your App.swift file. Don't forget to add launchViewModel as
an environmentObject to your ContentView().
© Design+Code
Back in your LaunchViewModel class, add an init() and call your fetch() function so the data is fetched when the
LaunchViewModel is initialized in the App.swift file.
Now run you app (⌘ + ⇧ + R). When the app launches, you should see the result of the GraphQL query in your console.
© Design+Code
Now that we got our data printed on the console, we need to process it into data we can actually use in our application.
Create a Model
A Model is where you define your app's data types. Storing the Models outside of the View makes your code more
readable and is easier to test. Following Apollo's Launch playground schema, we will build our Launch type structure.
Create a new file called LaunchModel.swift, and paste the following data model for Launches:
© Design+Code
Process the data
Now that we have both the data from GraphQL and the Model, we simply need to create a function to process the data
and transform it into the Launches data type we just created.
In LaunchViewModel.swift, create a new variable at the top to store the data we will process. It will be an empty array to
start with.
Then, create a new function to process the data we get from GraphQL. Here, we are dumping the data we get from
GraphQL into our model and it will process it for us.
We will need to change a little bit of the fetch function in LaunchViewModel.swift so that we can process our data. Right
after the case .success(let graphQLResult) line, remove the current print and paste the following code:
© Design+Code
Here, we are making sure that we do get data from our Network call, then we're processing it, turning it into our Launches
data type, and saving it into the launches variable defined at the top.
Conclusion
We're almost there! In the last section of this three-part Apollo GraphQL tutorial, we'll learn how to display the data in our
View. See you in the next section
© Design+Code
!
16 Apollo GraphQL Part 3
Apollo GraphQL is a platform that lets you do API calls using GraphQL. It's the modern way to fetch data, as it deals with
asynchronous fetching and only fetches the data you really need. In this last section, we will display the data we fetched
with Apollo GraphQL in our View.
If you haven't, I highly recommend you to follow the first part of this Apollo GraphQL tutorial to learn how to install Apollo
GraphQL into your Xcode project and the second part to learn how to fetch and process your data.
To start off, we will need our LaunchViewModel in our View, so let's add it as an EnvironmentObject at the top of our
View:
Then, we will call the launches variable we defined in our LaunchViewModel and loop through the array:
This is good if we only want to display a single line of information. But if we want to display all the information we get from
the server, it would be good to create a new component and pass the data to that component, like so:
© Design+Code
This means that we must pass a launch instance to the LaunchCard. Then, you can just call each launch variable and get
all the information we got from the server.
Since we created our data type, when we type a . after launch, the autocompletion will suggest all the fields from the
Launches data type we created in section two of this tutorial. Isn't that amazing?
The preview might always pause after adding the Apollo script. To prevent this from happening, remember to check the
For install builds only option, just below the script. Don't forget to uncheck it every time you add a new query or update
a query so Apollo can re-generate the API.swift file.
In ContentView.swift, make sure to add the LaunchViewModel() environmentObject to the preview, since the preview
is a completely different entity from the App and needs its own environmentObjects.
© Design+Code
In LaunchCard.swift, you need to pass a launch variable. Read the section Data from JSON to learn how to load data
from a JSON file. Create the launchData.json and paste the following JSON into it:
Then, simply save the data you load from this JSON file into a variable called previewLaunchData.
In your LaunchCard preview, pass in the previewLaunchData variable and your preview should work.
Conclusion
To sum up, we learned, through this three-part Apollo GraphQL tutorial, how to install Apollo GraphQL in our Xcode
project, how to fetch and process the data, as well as how to display it in our View. I hope you enjoyed this tutorial and
learned something new
© Design+Code
!
17 Configuration files in Xcode
CREATE CONFIGURATION FILES AND ADD VARIABLES DEPENDING ON THE ENVIRONMENT - DEVELOPMENT OR PRODUCTION
It's good practice when coding to separate your configuration settings from your code. Similar to a .env files, Xcode
comes with .xcconfig files, but are way more powerful than .env files. Let's see how to add a configuration file to our
Xcode project and include in it different configuration variables, depending on the environment.
In the Filter field, type config and select the first result, Configuration Settings File.
© Design+Code
Name your file Development for your development environment.
Then, navigate to your project. Under Configurations > Debug, select Development. This means that in development
mode (or debug mode), we want to use the configuration in the Development.xcconfig file.
© Design+Code
You can create another file and follow the same steps, but name it Production and set the configuration to release mode.
To set up different variables depending on the environment you're working on (development or production), you can add
the following code to your _ files:
The variables in the .xcconfig files are only available at build time. In order to pull them out programmatically, you'll need
to add them to your Info.plist file. If you develop for different OS (iOS, macOS), you'll need to add the keys to each of
your Info.plist file.
In your Info.plist file, hover over Information Property List and click on the + button. You can also right-click Information
Property List > Add Row.
© Design+Code
Create a Configuration.swift file
Next, create a new file in the Shared folder, named Configuration.swift. Copy and paste this code:
© Design+Code
Access your variables
Then, to access your base url depending on your environment, simply add this code:
© Design+Code
18 App Review
Asking for a rating and reviews from your users can encourage people to download your application, and can also help
with its notoriety. In order to add this functionality in your application, you'll need to use the SKStoreReviewController API.
The SKStoreReviewController lets you ask for a review from your users to up to three times in a period of 365 days.
Setup
Create a file called RequestReview.swift and import SwiftUI as well as StoreKit at the top.
The basics
© Design+Code
The main function to call is the .requestReview() function on SKStoreReviewController. However, we need to specify
the scene in .requestReview(), so we'll wrap it like so:
It's better user experience to not show the review request too many times. In order to show it only after a certain amount
of time, we need to get the build number, as well as the app version the user is using. We will also create a
runsSinceLastRequest variable that will increment every time the user launches the app.
We will also add a limit and set it to 10. When runsSinceLastRequest reaches 10, we will want to show the review request
to the user.
Next, back in the showReview() function, we will need to increment the runsSinceLastRequest every time the function
is called. We will also need to get the app version and the build version.
© Design+Code
We'll create a currentVersion variable, which will be a string that contains the appBuild as well as the appVersion.
Just before the if statement, we need to add some guard statements to only show the review alert when the version isn't
equal to the currentVersion, and when the runsSinceLastRequest is equal to the limit we previously set.
Inside the if statement, we will reset the runsSinceLastRequest to 0 and set the version to the currentVersion.
© Design+Code
Call the function
You'll simply need to call the function onAppear on your View. The review alert will appear after the number of times you
set for the limit variable. In our case, it's set at 10.
© Design+Code
19 ImagePicker
CREATE AN IMAGEPICKER TO CHOOSE A PHOTO FROM THE LIBRARY OR TAKE A PHOTO FROM THE CAMERA
Often times, you would want to add the feature in your application to change the user's profile picture. All you need to do
is create an ImagePicker, which is a UIViewControllerRepresentable, and call it in your View. Let's do just that!
Firstly, you'll need to create the ImagePicker. It's a ViewController that will enable you to show the user's photo library or
their camera in order to change their profile picture. We need to add a sourceType to specify if we want to pick from the
library or the camera. Also, the selectedImage and the Coordinator will enable us to preview the selected image in the
View.
© Design+Code
Note: By default, the ImagePicker will only enable the user to choose from pictures or take a picture - videos are not
included.
Add states
In your View, add the following states. The image variable is where we'll save the selected image and pass it to our
ImagePicker. The second state, showSheet, will be toggled to show the ImagePicker as a sheet, on top of your View.
Create a button
Create the button on which the user will tap to show the ImagePicker.
© Design+Code
Create an Image
Add an Image to your View so the user can preview the image they just selected.
© Design+Code
Toggle the state
OnTapGesture of the Button, change the showSheet state to true so we can show the sheet by listening to this state.
Add the sheet modifier that will listen to the showSheet state and show the user's photo library when showSheet is set to
true.
If you wish to take photo from camera, simply change the sourceType to .camera.
© Design+Code
Don't forget to add in your Info.plist a new key/value pair to ask the user permission to access their camera. Under the
iOS folder, click on Info.plist. Add a new key, and name it Privacy - Camera Usage Description. Under the value column,
specify why you need to use the camera.
Note: It's not possible to test the camera on the Simulator. You'll need to connect your device to your Mac in order to test
it.
Final code
© Design+Code
© Design+Code
20 Compress a UIImage
Before saving your image into your database, you would want to compress it so the file size isn't too big, as you wouldn't
want to a small profile picture to take too much space in your database. Let's learn how to compress your UIImage by
converting it into a JPEG file, reducing its quality and its size.
Create an UIImage
In order to convert an image to JPEG, you'll need an UIImage. Learn how to create an UIImage after the user selects a
photo from their library or take a photo with their camera by reading the ImagePicker section of this handbook. For now,
we'll use an instance of UIImage.
Create an extension
Reduce the image height so it becomes smaller and uses less space.
Create a new file called Extensions.swift and import SwiftUI at the top. Then, create an extension for UIImage, called
aspectFittedToHeight, which will take a newHeight and return a smaller UIImage.
Create a function
Create a function to call the aspectFittedToHeight() extension we just created. It'll return a compressed UIImage with
reduced file size.
© Design+Code
Convert to JPEG and compress the quality
Then, use the built-in jpegData(compressionQuality:) function to convert your UIImage into JPEG, which will reduce the
file size, and compress the quality of the image. It will return a data object, containing the image, and you'll be able to
save to your database.
The compressionQuality is a CGFloat between 0 and 1, 1 being the highest quality possible and 0 the lowest quality. If
you don't need your users' profile pictures to be high-quality, you can set it to 0.1 or 0.2 so the file size is at its lowest
© Design+Code
.
21 Firebase Storage
While developing your SwiftUI application, you might want to save photos the user provides in your database. For
example, a user changes their profile photo and you need to save it somewhere. Luckily, if you already have Firebase
enabled in your project, you can use Firebase Storage to store your photos. Let's see how to integrate Firebase Storage to
our project, upload a new file, list the files we have in our database, and delete one.
Learn how to enable users to add their own photos in your application by reading the ImagePicker section of this
handbook and compress an image before uploading it in the Compress a UIImage section.
If you don't already have Firebase in your project, read the Firebase Auth section of this handbook to learn how to enable
it. After you've added the Firebase Swift Package (https://github.com/firebase/firebase-ios-sdk.git) to your project,
remember to check the FirebaseStorage library option.
Create a StorageManager
Create a StorageManager.swift file and import SwiftUI and Firebase at the top.
© Design+Code
After the import, we'll create a class that will conform to an ObservableObject and initialize Firebase Storage.
You can easily upload a UIImage to Firebase Storage with the putData:metadata:completion: method from Firebase.
Let's start by creating our function. It'll take an image as an argument, which is of UIImage type.
Inside the function, we need to create a reference. A reference is the path in which we will save the photo. Here, we want
to save the photo under the images folder, and rename the file image.jpg.
Next, we need to compress and convert the image passed to our function into a JPEG file. Read more about it in the
Compress a UIImage section of this handbook.
Change the type of file we're going to upload in Firebase Storage's metadata. If you don't change it, the file saved in
Firebase Storage will be of type application/octet-stream.
© Design+Code
Lastly, we'll upload the image using the putData:metadata:completion: method from Firebase.
Since the .jpegData(compressionQuality:) function returns an optional Data (Data?), we need to unwrap that optional
with an if statement. If there's an error, we'll print it in the console. If all go as planned, the metadata should be printed in
the console.
List items
You can list all the items in your Firebase Storage with the listAll(completion:) method from Firebase. You can also call the
list(withMaxResults:completion:) to list a limited amount of files from your database.
Create a storage reference. We want to list all files from the images folder.
Then, just call the listAll function from Firebase. If there's an error, we'll print it. If there's no error, we'll print each item we
get from the images folder in Firebase Storage.
© Design+Code
You can also list just one item with the list(withMaxResults:completion:) method:
Delete an item
To delete an item, you can use the listItem() function we just create to get the storage reference of the item you want to
delete.
© Design+Code
Update rules
Don't forget to update your Firebase Storage rules to allow users to read and write to the Storage. Navigate to the
Firebase Console > Storage > Rules and update the rules depending on your app's needs.
To use your functions in your View, simply create a new instance of StorageManager at the top of the file.
Then, you can call all the functions we created in StorageManager.swift on the storageManager variable we just
created in the View. For example, I can listen to the change of an image and upload it to Firebase Storage:
© Design+Code
Or you can list all items in Firebase Storage onAppear:
Final code
© Design+Code
© Design+Code
© Design+Code
22 Search Feature
IMPLEMENT A SEARCH FEATURE TO FILTER THROUGH YOUR CONTENT IN YOUR SWIFTUI APPLICATION
A search feature is essential in any application. In SwiftUI, it's very easy to implement that by simply filtering your content
with the text the user inputs in the search field. For this tutorial, we will be fetching 30 random coffees from the Random
Data API. I highly encourage you to follow the Data from JSON and the HTTP Request sections of this handbook before
following this tutorial, where I go more in depth about certain subjects we'll cover here.
Before everything, we need to create our data model first. We will making an HTTP request to the https://random-data-
api.com/api/coffee/random_coffee?size=30 endpoint to get 30 random coffees. If you click on the link, you'll see an array
of objects, and each object represents a coffee.
Note: I'm using a Chrome extension called JSON Formatter to format how the JSON is displayed in my browser. If you
don't have the extension, you can get it in the Chrome Web Store.
Let's take a coffee object as an example and create our coffee model:
© Design+Code
Each coffee has an ID, a UID, a blend name, an origin, a variety, some notes and an intensifier.
In the JSON, the blend name is specified as blend_name. We must conform our model's variables to exactly the same as
the JSON, so that the JSON decoder works. However, in Swift, the convention is to always use CamelCase and not
snake_case, so we're creating an extension on Coffee, adding the blendName variable, and returning the blend_name.
Therefore, we'll be able to call coffee.blendName in our View.
Create a ViewModel
Now that we got our Coffee model, we can create our ViewModel. The ViewModel will contain all the functions related to
the data fetching, processing and filtering. Create a new file named CoffeeViewModel.swift. This file will contain a class
that will conform to the ObservableObject protocol.
Create a variable allCoffees, where we'll store all the coffees we will get from our HTTP request. Also, create a
@Published filteredCoffees variable. This is the variable we'll be using to display our coffees in the UI, and also the
variable that we'll be using to filter the coffee when the user is searching.
© Design+Code
Next, create your fetchCoffees function. Read the section HTTP Request to learn more about how to create an HTTP get
function in SwiftUI.
We'll be saving the response's data into the allCoffees variable. Initially, the filteredCoffees array will contains
allCoffees, so we'll simply assign filteredCoffees to allCoffees.
© Design+Code
Then, call your fetchCoffees functions on initialize of the CoffeeViewModel:
Now it's time to link our CoffeeViewModel to our app! In ProjectNameApp.swift, initialize your ViewModel and attach it
to your ContentView as an environment object:
© Design+Code
Display the data
Display the data in your view. Create a CoffeeRow that takes a coffee as an argument and displays its blendName, origin
and notes.
You might run into an error when trying to build for the preview. To make it work, head over to the Data from JSON section
of this SwiftUI Handbook to learn how to load local data from a JSON file into your SwiftUI application.
In ContentView, create a ScrollView and iterate over the filteredCoffees from the CoffeeViewModel.
© Design+Code
Add the search feature
Next, we'll add the search feature in our CoffeeViewModel. Add a @Published searchText variable at the top of the
class.
Create your filterContent function. This function will check for all the matching coffees in the allCoffees variable, and
return the matching coffees if the searchText the user inputs is higher than one character. Otherwise, the function will
return all the coffees available.
© Design+Code
Create your search input
Create a TextField. The bound value of the TextField will be the @Published searchText from our CoffeeViewModel.
On change of the searchText, we'll call the filterContent function we created in CoffeeViewModel. The changes in the
filteredCoffees array will automatically be reflected in our view, because the filteredCoffees variable is @Published.
Add a condition
In your ScrollView, add a condition to only iterate over filteredCoffees if the array contains one or more items. If not,
we'll display a Text that says No coffee found.
Congratulations! You can now test your coffee application by running it in the Simulator! If you wish to test in the preview,
remember to press the play button in order to fetch the data from the API. The final code for the CoffeeViewModel and
ContentView can be found below.
© Design+Code
Final code
© Design+Code
The final code for ContentView is:
© Design+Code
© Design+Code
The final result for the coffee application looks like this, in dark mode:
© Design+Code
23 Push notifications Part 1
SET UP FIREBASE CLOUD MESSAGING AS A PROVIDER SERVER TO SEND PUSH NOTIFICATIONS TO YOUR USERS
Push notifications (or remote notifications) are a core part of any application. They are notifications sent to your users
through your provider server and the Apple Push Notification service. Push notifications might seem scary at first since
they require a lot of setup, from Authentication keys, setting up your own server, asking for permission from the user, to,
finally, sending the notification itself.
Luckily, if you already have Firebase enabled in your project, you can use Firebase Cloud Messaging to send push
notifications to your users through the Firebase Console. In this first section of the Push Notification series, let's see how
to setup Firebase Cloud Messaging and enable push notifications in your Xcode project.
Add Firebase
Make sure you already have Firebase enabled in your project. Read the Firebase Auth section of this handbook to learn
how to set up Firebase in your SwiftUI project. When adding the package to your project (https://github.com/firebase/
firebase-ios-sdk.git), make sure you check the FirebaseMessaging and FirebaseAnalytics options so you can connect
to Firebase Cloud Messaging in your app.
Before we continue, make sure that you are part of the Apple Developer Program, as some features aren't available to free
users. You can check your program type by going to your Apple Developer Account > Membership. If you aren't part of
it, you can enroll here.
© Design+Code
Get an APNs Authentication key
In order to be able to send push notifications to your users, you'll need to create an Apple Push Notifications service key.
To do so, navigate to https://developer.apple.com/account to access your Developer account.
Click on Certificates, Identifiers & Profiles > Keys and add a new key by clicking on the blue + sign next to the Keys
title. Enter a key name, check the Apple Push Notifications service (APNs) option, then download you APNs
Authentication Key - save it in a secure place, as you'll need it later and it's a one-time download only!
You are allowed a maximum of two Keys for APNs. If you have already reached the maximum, revoke an old key that you're
not using and create a new one.
Using the APNs Authentication Key we just created at the step above, we'll need to link it to Firebase Cloud Messaging. To
do so, head on to your project's Firebase console. Next to Project Overview, click on the gear icon > Project settings.
Click on Cloud Messaging and scroll down to iOS app configuration.
© Design+Code
Under APNs Authentication Key, click on Upload and upload the .p8 file you just downloaded at the step above. The name
of the required file is something like AuthKey_28D9YRGAO03.p8.
© Design+Code
You'll also need to provide your Key ID and Team ID.
Find the Key ID in your Apple Developer Account, under Certificates, IDs & Profiles > Keys. Click on YourKeyName, and
you'll find your Key ID there.
Next, you'll need to add capabilities to your project so you can send notifications to your users. Make sure that you are
part of the Apple Developer Program, as some capabilities aren't available to free users. Check the available capabilities
for your account type here.
If all is set and you're under the Apple Developer Program, head back to your Xcode project and navigate to your App >
iOS > Signing & Capabilities. Add a new capability by clicking on + Capability.
Add Push Notifications and Background Modes to your project. Under Background Modes, check Background fetch
and Remote notifications. This will allow us to send notifications from our server (Firebase Cloud Messaging) to the user.
When everything is set up, you can move on to the next section to learn how to create an AppDelegate and start sending
test notifications from Firebase Cloud Messaging to your device
© Design+Code
!
24 Push notifications Part 2
CREATE AN APPDELEGATE TO ASK PERMISSION TO SEND PUSH NOTIFICATIONS USING APPLE PUSH NOTIFICATIONS SERVICE AND FIREBASE
CLOUD MESSAGING
Now that we have APNs and Firebase Cloud Messaging set up in order to send push notifications to our users, we can
move on to the next part of the Push Notifications tutorial - that is, the coding part! We will use the AppDelegate method,
as specified in Firebase's documentation, to ask the user for permission to send them push notifications, then get the
device token to send a test notification to our device.
If you haven't, I highly suggest you to read the first section of this Push notifications series to learn how to setup Firebase
Cloud Messaging and APNs in your application.
The AppDelegate is an object that manages your app. It listens to when your app is launched, when it's receiving data, or
the state of the app - if it's in the background or in the foreground, for example. The AppDelegate is useful for
notifications as there are methods, like didReceiveRemoteNotification, that will tell the app that a remote notifications has
arrived.
To start off, let's import these three elements at the top of our ProjectNameApp.swift file:
Then, we'll need to create our AppDelegate. Create it below your App structure.
© Design+Code
Here, we created two functions. The first one is executed once the app finished launching. We are configuring Firebase,
then asking the user for permission to send them push notifications. The second function listens to remote notifications
and will alert the app when a new push notification comes in.
Next, let's create an extension on AppDelegate. The messaging() function inside of this extension will print the device
token. It'll be useful when we'll be sending a test notification through the Firebase Cloud Messaging Console to our
device.
© Design+Code
Finally, we'll create one last extension on AppDelegate. It's a UNUserNotificationCenterDelegate and you can think of it as
a Notification Center. This is where all the notification actions are handled. Don't forget to add @available(iOS 10, *)
before the extension as it's only available after iOS 10.
© Design+Code
Link AppDelegate to App
Add the var appDelegate to your app structure in order to link the AppDelegate to your App.
Now, build and run (⌘ + R) you app. When you build your app and run it for the first time after adding the AppDelegate
code above, you'll see an alert asking you for permission to send you push notifications. Click on Allow then head back to
Xcode.
Note that it's best to test on a device connected to your Mac and not the Simulator, as the test notifications we'll send
won't work on the Simulator.
© Design+Code
In the console in Xcode, you'll see that a token has been printed. This token is a unique token that will allow Firebase
Cloud Messaging to send a push notifications to your device. It's like an address to your device. Copy that token because
we'll need it in the next step.
Now, the coding part is done. Yay! Let's head back the the Firebase Console. Under Engage, click on Cloud Messaging.
In the banner, click on Send your first message. It'll lead you to the Notifications Composer.
© Design+Code
Fill in the Notification title and Notification text. You can fill in the other fields if you wish. On the right, you'll see a Send
test message blue button. Click on that. In the Add an FCM registration token field, paste the token you copied from
the Xcode console and click on +, then Test.
And ta-dah! Now you've just received your first push notification sent from Firebase Console! If you haven't, it can be
tricky to debug and see what went wrong because you don't get any error message. Scroll down to the Debugging
section below to have some leads as to why you're not receiving any notification.
© Design+Code
If everything is working fine, you can move on to the last section of this Push notifications series to configure sending
notifications for production.
Debugging
You're not receiving any notification on your device? Here are some leads as to why you can't receive your notification:
• Stop the current build, and rebuild your project again on your device.
• Make sure that your Bundle Identifier (or BundleID) in Firebase is the same as the one in your Xcode project.
• Make sure you added the APNs authentication key in Firebase. If you haven't, go to the Push Notifications Part I of
this series to learn how to create one and upload it to Firebase Cloud Messaging.
• Make sure that you copied all the AppDelegate code and added the two extensions.
• You are testing on the Simulator - notifications only work on an actual device, so connect a device to your Mac and
test again.
• You didn't copy the exact device token - make sure you copy the entire token and paste it in Firebase Cloud
Messaging.
• Your device doesn't have an activated SIM card - make sure you have an activated SIM card in your device or that
you're connected to a mobile data service to receive push notifications.
© Design+Code
• Your Wifi network is blocking the push notifications - connect to another Wifi network or to your mobile data service
in order to receive notifications.
• As per Firebase's documentation, you need to make sure you app is in Background mode - that is, make sure your
app is not open nor in the foreground. After your app has launched, simply click on the Home button once.
• You can also read this article on the Firebase Blog to help you debug further
© Design+Code
.
25 Push notifications Part 3
TIE EVERYTHING TOGETHER AND TEST YOUR PUSH NOTIFICATIONS FEATURE IN PRODUCTION
In this last section of this Push notifications series, we'll look at the last setup steps to enable push notifications in our
application in production mode - that is, in beta testing or when the app is released to the public in the App Store. We'll
also send our first notification campaign using the Notifications Composer in the Firebase Console.
If you haven't, I encourage you to read the first two sections on the Push notifications series - Push notifications Part I and
Push notifications Part II - to learn how to setup Firebase Cloud Messaging, APNs and add the code in AppDelegate to
enable notifications in your app.
Firebase Analytics
Firebase Cloud Messaging works hand in hand with Firebase Analytics, so to make sure that FCM works correctly in
production, make sure that you add the Firebase Analytics package in your project and enable it in the Firebase Console
as well.
If the Firebase Analytics package is not in your Xcode project, navigate to ProjectName > Targets > iOS > General >
Frameworks, Libraries, and Embedded Content.
© Design+Code
However, at the time of writing (March 2021), there is a known issue with Firebase Analytics that might raise a validation
error when archiving your project. The error displayed is Found an unexpected Mach-O header code: 0x72613c21. As a
workaround to solve this issue, navigate to ProjectName (iOS) > Edit Scheme > Archive > Post-actions > + > New Run
Script Action.
© Design+Code
Make sure to select your target. In our case, it'll be iOS.
Then, add the following script. This script will remove all the unnecessary artifacts from the Firebase Analytics package
that are causing the validation error. This won't affect your project nor the notifications feature.
© Design+Code
Entitlements file
In your ProjectName.entitlements file, you'll need to change the APS environment key to production.
According to Apple's documentation, the APS Environment Entitlement is a key that is automatically generated by Xcode
when you enable Push notifications in your project and have a development provisioning profile. By default, the APS
environment value is set to development. When you release your app for beta testing or in the App Store, you'll need to
set the APS environment to production.
Finally, after everything is set up, simply release your application - either in the App Store or on Test Flight.
Then, navigate to the Notification composer in the Firebase console and create a new notification campaign. Fill in the
Notification title and text in the first step.
© Design+Code
In the second step, select your application if you want to send the notification to all users, or click on Topic and select the
topic you want to target.
If you want to send the notification at a certain time in the future, change it in the third step.
In the fourth and last step, you can add additional options. For example, you can add a sound to your notification, an iOS
badge (a little number in red will be displayed in the top right corner of your app's icon), and an expiration date. The
expiration date is the time when the campaign will end, and FCM will stop trying to send notifications to users. The
maximum duration life of a campaign is 4 weeks.
© Design+Code
After you finished composing your first notification campaign, click on the Review button at the bottom and then Publish.
If all is well, you and all the users who allowed notifications for your app should instantly receive the notification on their
device.
Congratulations! You just finished the push notifications feature in your application! From now on, you can simply send
notifications to your users using the FCM Notification Composer and users will automatically receive them! 🎉
© Design+Code
26 Network connection
VERIFY THE NETWORK CONNECTION OF YOUR USER TO PERFORM TASKS DEPENDING ON THEIR NETWORK'S REACHABILITY
Making your app available offline is one of the must-have features any app should offer nowadays. In order to add this
feature, you need to know if the user's network connection is reachable (if they're connected to the Internet) or not. It's
easy to create a NetworkReachability class that will check if the network is reachable.
Imports
Let's create a new file called NetworkReachability.swift and import Foundation and SystemConfiguration at the top of
our file.
Next, create your NetworkReachability class. It'll conform to an ObservableObject, so that the changes will
automatically be accessible to your entire application.
Add variables
At the top of your class, add two new variables. The first one is a @Published variable, called reachable, that will be true
if the network is reachable. Initially, it'll be false. We're also adding the private(set) so that we can only modify this variable
inside of the NetworkReachability class.
The second variable is a private variable called reachability. It's a SCNetworkReachabilityCreateWithName, which takes a
hostname and it creates a reachability reference. We'll need it later when we'll check the Internet connection.
© Design+Code
Create the isNetworkReachable function
Create your first function and call it isNetworkReachable. It'll take flags, which are of type SCNetworkReachabilityFlags,
as an argument, and return a boolean that will tell us if the network is reachable.
The flags passed as an argument inside of the function can contain different constants. If you go on the
SCNetworkReachabilityFlags documentation page, you'll see the list of possible constants that the flags can contain.
© Design+Code
So, inside of the isNetworkReachable function, let's check if the flags contain some constants that are useful to us in
order to check the Internet connection. On the last line inside of this function, we'll return the Boolean that will tell us if
the network is reachable. The network is considered reachable if isReachable is true, no connection is required and the
device can connect without intervention to the network.
Next, let's create the checkConnection function that will return us the connection status, as a Boolean.
Inside of this function, we are creating a SCNetworkReachabilityFlags variable called flags, and then passing them to
SCNetworkReachabilityGetFlags. This takes the reachability variable we created at the beginning as well as the flags
variable. Finally, we're checking if the network is reachable with the isNetworkReachable function. If it's reachable, we're
returning true. If not, we're returning false.
© Design+Code
Call checkConnection on initialize
On init of the class, we'll call the checkConnection function, and save the result in the reachable variable at the top of
the class.
To use the NetworkReachability class, initialize it wherever you want and simply use the
networkReachability.reachable Boolean to perform any action you want when Internet is available, or not!
Final code
© Design+Code
© Design+Code
27 Download files locally Part 1
DOWNLOAD VIDEOS AND FILES LOCALLY SO USERS CAN WATCH THEM OFFLINE
If you're developing an application based on videos, like the DesignCode app or the YouTube app, one must-have offline
feature is to allow users to download the videos for offline viewing. It's quite easy to implement. Let's see how we can
create a DownloadsManager that will take care of the downloading, saving locally, and deleting of videos.
Setup
Before starting, if you want to offer this feature for offline mode, I suggest you to read the section on Network
Reachability to learn how to create a NetworkReachability class to check the user's internet connection.
This @Published state will let us know if the video file is currently downloading, so we can show a loading indicator to the
user while it's being downloaded.
© Design+Code
First, inside of downloadFile, we're setting the isDownloading state to true.
Then, we're creating a docsUrl variable. It's a link to the device's file manager, under the user's personal files. Then, we're
creating a destinationUrl. The String we're passing to the appendingPathComponent function is the name we'll give to
our file.
Next, we're unwrapping the destinationUrl, to make sure that it exists before continuing. Inside of the if statement, we're
checking if the file is already downloaded - if it is, we won't download it again.
© Design+Code
In the else statement, we're creating an HTTP request to get the video file. Make sure the link you provide is a link to the
actual mp4 file. Links to HLS links can't be downloaded with this method. Once we get the data, we're writing it locally.
The file will be saved on the device, inside of the application's .ipa file.
© Design+Code
Delete the file
If you want to allow users to delete the file, use the following function.
If you need to check if the file is already downloaded, create a @Published variable that tells you if the file is downloaded.
© Design+Code
Then, use the function below. It'll let you know if the file already exists in the FileManager, and save it in the
isDownloaded variable.
To get the downloaded video and get the AVPlayerItem in return, use the following function. Remember to import AVKit
at the top of the file. You can read more about integrating a video player in your application with the Play Video with
AVPlayer section of this handbook. When creating your AVPlayer instance, simply replace url with the playerItem you get
from this function.
© Design+Code
Final code
© Design+Code
© Design+Code
© Design+Code
28 Download files locally Part 2
LEARN HOW TO USE THE DOWNLOADMANAGER CLASS IN YOUR VIEWS FOR OFFLINE VIDEO VIEWING
In the first part of this two-part series about downloading files locally, we created our DownloadManager class where we
added the functions to download, delete, check if the file is downloaded and get the video file asset. In this last section,
we'll see how we can use the DownloadManager in our views.
Create a DownloadButton
Next, create a button that will let users download the video.
© Design+Code
© Design+Code
Create a WatchButton
Create another button. This button will display a VideoView when the user clicks on it.
© Design+Code
Create a VideoView
This VideoView is using AVPlayer to display the video file asset we downloaded.
Finally, add everything in ContentView. Only if the video is downloaded, we'll display the WatchButton. OnTapGesture
of the WatchButton, we'll display a fullScreenCover containing the VideoView. OnAppear of ContentView, we're also
checking if the file is already downloaded.
© Design+Code
© Design+Code