Design+Code logo

Quick links

Suggested search

Disclaimer

This tutorial assumes that you already know how to fetch data from a server using Apollo GraphQL. You can learn how by following the three part Apollo GraphQL tutorial of this handbook (Part 1, Part 2 and Part 3).

Add ApolloSQLite library

If you've followed the three-part tutorial on Apollo GraphQL mentioned above, you should already have the Apollo SDK installed in your Xcode project. For now, only the Apollo library should have been included in your project. However, to be able to cache data locally on the user's device, we'll need to add another library, which is the ApolloSQLite one.

Open your Xcode project, click on your ProjectName > Targets > iOS > General > Frameworks, Libraries, and Embedded Content. You should see the Apollo library there.

Apollo Library

Click on the + sign at the bottom of the list. In the modal that appears, search for ApolloSQLite and add it to your project.

ApolloSQLite

Apollo's caching system

With Apollo, we can cache our data two ways: InMemoryNormalizedCache or SQLiteCache.

  • InMemoryNormalizedCache is the default way of caching data with Apollo. It caches the data until the user kills the app. At the next launch, Apollo will fetch the data again if there's a network connection. This caching strategy is included in the Apollo library, so if you've followed the 3-part tutorial, this is the strategy that is used in your app.
  • SQLiteCache is included in the ApolloSQLite library. This method create a SQLite file within the app - which means the app size will be bigger. However, saving everything in a SQLite file locally means that we don't need a network connection to fetch the data again, which is good for when users are offline, as they can still navigate the app without Internet. This is the method we'll be exploring in this tutorial, and that's why we added the ApolloSQLite library to our project.

You can read more about Apollo's caching system in Apollo GraphQL's documentation.

Specify the caching method

To cache data locally, we'll need to set our preferred caching method to SQLiteCache. If you remember, we created a file called Network in which we initialized our ApolloClient. We only passed the URL of our server to ApolloClient.

ApolloClient

Now, we'll create another client and, instead of passing a URL, we'll specify the caching method we want. Don't remove any code that's already in the Network class. We'll create another client on top of the default one.

First, we'll import ApolloSQLite.

// Network.swift

import ApolloSQLite

Right below the first ApolloClient we created, let's create another one called apolloSQLite. We'll set it equal to a closure with parentheses in the end, because we'll need to do a bit of setup first before returning the actual ApolloClient instance.

// Network.swift

private(set) lazy var apolloSQLite: ApolloClient = {

}()

We'll then need to specify where we want to save the SQLite file that'll be created by ApolloSQLite. Since accessing the documents directory from FileManager might throw an error, we need to add the try keyword and wrap everything in a do catch statement. We'll be saving our apollo_cache.sqlite file in the Documents directory of our application.

// Network.swift

do {
		var documentsPath = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
		let fileUrl = documentsPath.appendingPathComponent("apollo_cache.sqlite")    
} catch {
		print("Error creating ApolloSQLite Client: \(error)")
}

Next, we'll need the fileUrl to initialize the SQLite cache.

// Network.swift

let sqliteCache = try SQLiteNormalizedCache(fileURL: fileUrl)

Once this is done, let's create an ApolloStore with the sqliteCache we juste created.

// Network.swift

let store = ApolloStore(cache: sqliteCache)

If we take a look at Apollo's documentation on the ApolloClient class, we have two ways of instantiating an ApolloClient. One is by only providing the URL of our server, and the other one is by providing two arguments: a NetworkTransport and an ApolloStore. We'll be creating the ApolloClient the second way, as we need to specify the caching method as SQLiteCache. We already have the ApolloStore, but not the NetworkTransport. Let's create that.

NetworkTransport

A NetworkTransport is how you send your fetch request to the server. With Apollo, we can send the request three different ways:

  • With RequestChainNetworkTransport, which is the standard way and uses HTTP to send the request.
  • With WebSocketTransport, which uses WebSocket and allows us to get instant updates from the server. If you use this method, you'll need to add the ApolloWebSocket library to your project.
  • With SplitNetworkTransport, which is a mix of RequestChainNetworkTransport and WebSocketTransport. If you use this method, you'll need to add the ApolloWebSocket library to your project.

You can read more about NetworkTransport in Apollo's documentation about Advanced Client Creation. In our case, we'll use the RequestChainNetworkTransport method. We'll pass a DefaultInterceptorProvider with our store as the interceptorProvider, as well as the endpointURL of our server.

// Network.swift

let normalTransport: RequestChainNetworkTransport = RequestChainNetworkTransport(interceptorProvider: DefaultInterceptorProvider(store: store), endpointURL: URL(string: url)!)

Finally, we'll return an ApolloClient instance created by passing the normalTransport variable and the store as arguments.

// Network.swift

return ApolloClient(networkTransport: normalTransport, store: store)

Since we're in a do catch statement, the catch statement doesn't return anything, and Xcode is complaingin. Even if creating the apolloSQLite client fails, we also need to return an ApolloClient.

ApolloClient Error

Let's return the ApolloClient we created by using the server URL, that we named apollo.

// Network.swift

} catch {
		print("Error creating ApolloSQLite Client: \(error)")
		return apollo
}

Below is the final code for the apolloSQLite instance that we're creating.

// Network.swift

private(set) lazy var apolloSQLite: ApolloClient = {
    do {
        let documentsPath = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        let fileUrl = documentsPath.appendingPathComponent("apollo_cache.sqlite")

        let sqliteCache = try SQLiteNormalizedCache(fileURL: fileUrl)

        let store = ApolloStore(cache: sqliteCache)

        let normalTransport: RequestChainNetworkTransport = RequestChainNetworkTransport(interceptorProvider: DefaultInterceptorProvider(store: store), endpointURL: URL(string: url)!)

        return ApolloClient(networkTransport: normalTransport, store: store)
    } catch {
        print("Error creating ApolloSQLite Client: \(error)")
        return apollo
    }
}()

Use apolloSQLite

The final step is to update the ApolloClient we're using in our view model to fetch the data. Let's go to the LaunchViewModel.

Use apolloSQLite

In our fetch function, instead of calling apollo, we'll be calling apolloSQLite, so that the data fetched is saved locally in the SQLite file.

// LaunchViewModel.swift

Network.shared.apolloSQLite.fetch(query: LaunchListQuery()) { result in
	// More code...
}

Test the code

Now, let's just test our code! Build your application once with a network connection, and make sure that all the data is displayed correctly. Then, build the app again, but this time, disconnect from the Internet. The data should have been saved in the SQLite file and you should data on your screen, even offline. Sweet!

Learn with videos and source files. Available to Pro subscribers only.

Purchase includes access to 50+ courses, 320+ premium tutorials, 300+ hours of videos, source files and certificates.

BACK TO

Network Connection Observer

READ NEXT

Create a model from an API response

Templates and source code

Download source files

Download the videos and assets to refer and learn offline without interuption.

check

Design template

check

Source code for all sections

check

Video files, ePub and subtitles

Videos

Assets

ePub

Subtitles

1

Firebase Auth

How to install Firebase authentification to your Xcode project

8:18

2

Read from Firestore

Install Cloud Firestore in your application to fetch and read data from a collection

8:01

3

Write to Firestore

Save the data users input in your application in a Firestore collection

5:35

4

Join an Array of Strings

Turn your array into a serialized String

3:33

5

Data from JSON

Load data from a JSON file into your SwiftUI application

5:08

6

HTTP Request

Create an HTTP Get Request to fetch data from an API

6:31

7

WKWebView

Integrate an HTML page into your SwiftUI application using WKWebView and by converting Markdown into HTML

5:25

8

Code Highlighting in a WebView

Use Highlight.js to convert your code blocks into beautiful highlighted code in a WebView

5:11

9

Test for Production in the Simulator

Build your app on Release scheme to test for production

1:43

10

Debug Performance in a WebView

Enable Safari's WebInspector to debug the performance of a WebView in your application

1:57

11

Debug a Crash Log

Learn how to debug a crash log from App Store Connect in Xcode

2:22

12

Simulate a Bad Network

Test your SwiftUI application by simulating a bad network connection with Network Link Conditionner

2:11

13

Archive a Build in Xcode

Archive a build for beta testing or to release in the App Store

1:28

14

Apollo GraphQL Part I

Install Apollo GraphQL in your project to fetch data from an API

6:21

15

Apollo GraphQL Part 2

Make a network call to fetch your data and process it into your own data type

6:43

16

Apollo GraphQL Part 3

Display the data fetched with Apollo GraphQL in your View

5:08

17

Configuration Files in Xcode

Create configuration files and add variables depending on the environment - development or production

4:35

18

App Review

Request an app review from your user for the AppStore

5:43

19

ImagePicker

Create an ImagePicker to choose a photo from the library or take a photo from the camera

5:06

20

Compress a UIImage

Compress a UIImage by converting it to JPEG, reducing its size and quality

3:32

21

Firebase Storage

Upload, delete and list files in Firebase Storage

11:11

22

Search Feature

Implement a search feature to filter through your content in your SwiftUI application

9:13

23

Push Notifications Part 1

Set up Firebase Cloud Messaging as a provider server to send push notifications to your users

5:59

24

Push Notifications Part 2

Create an AppDelegate to ask permission to send push notifications using Apple Push Notifications service and Firebase Cloud Messaging

6:30

25

Push Notifications Part 3

Tie everything together and test your push notifications feature in production

6:13

26

Network Connection

Verify the network connection of your user to perform tasks depending on their network's reachability

6:49

27

Download Files Locally Part 1

Download videos and files locally so users can watch them offline

6:05

28

Download Files Locally Part 2

Learn how to use the DownloadManager class in your views for offline video viewing

6:02

29

Offline Data with Realm

Save your SwiftUI data into a Realm so users can access them offline

10:20

30

HTTP Request with Async Await

Create an HTTP get request function using async await

6:11

31

Xcode Cloud

Automate workflows with Xcode Cloud

9:23

32

SceneStorage and TabView

Use @SceneStorage with TabView for better user experience on iPad

3:52

33

Network Connection Observer

Observe the network connection state using NWPathMonitor

4:37

34

Apollo GraphQL Caching

Cache data for offline availability with Apollo GraphQL

9:42

35

Create a model from an API response

Learn how to create a SwiftUI model out of the response body of an API

5:37

36

Multiple type variables in Swift

Make your models conform to the same protocol to create multiple type variables

4:23

37

Parsing Data with SwiftyJSON

Make API calls and easily parse data with this JSON package

9:36

38

ShazamKit

Build a simple Shazam clone and perform music recognition

12:38

39

Firebase Remote Config

Deliver changes to your app on the fly remotely

9:05