Learn how you can use Charles for iOS and macOS to inspect encrypted and unencrypted network traffic for both your own apps and third party apps.

Update note: Tom Elliott updated this tutorial for Xcode 10.2, Swift 5 and iOS 12. Aaron Douglas wrote the original.

Let’s face it — we’ve all written code that just doesn’t work correctly and debugging can be hard. It’s even more difficult when you’re talking to other systems over a network.

Fortunately, Charles Proxy can make network debugging much easier.

Charles Proxy sits between your app and the internet. You configure your Simulator or iOS device to pass all networking requests and responses through Charles Proxy, so you’ll be able to inspect and even change data midstream to test how your app responds.

You’ll get hands-on experience with this (and more!) in this Charles Proxy tutorial.

Getting Started

You first need to download the latest version of Charles Proxy for Mac (v4.2.8 at the time of this writing). Double-click the DMG file and drag the Charles icon to your Applications folder to install it.

Charles Proxy isn’t free, but everyone is given a free 30-day trial. Charles will only run for 30 minutes in trial mode, so you may need to restart it throughout this Charles Proxy tutorial.

Note: Charles is a Java-based app and supports macOS, Windows and Linux. This Charles Proxy tutorial is for macOS specifically, and some things may be different on other platforms.

Launch Charles. It should ask for permission to automatically configure your network settings. If it doesn’t, press Command-Shift-P to manually have Charles ask this permission.

Click Grant Privileges and enter your password if prompted. Charles starts recording network events as soon as it launches. You should already see events popping into the left pane.

Note: If you don’t see any events, you may have not granted permissions or may have another proxy already setup. See Charles’ FAQ page for troubleshooting help.

The user interface is fairly simple to understand without too much experience. Many goodies are hidden behind buttons and menus, however, and the toolbar has a few items you should know about:

The first “Broom” button clears the current session and all recorded activity.

The second “Record/Pause” button will be red when Charles is recording events, and gray when stopped.

The middle buttons from the “Tortoise” to the “Check mark” provide access to commonly-used actions, including throttling, breakpoints and request creation. Hover your mouse over each to see a short description.

The last two buttons provide access to commonly-used tools and settings.

For now, stop recording by clicking the red Record/Pause button.

Under the toolbar is a toggle between Structure and Sequence. With Sequence selected, the top pane contains a summary of all the recorded network requests while the main pane contains detailed information about the selected request.

When Structure is selected, the top pane is replaced by a left-hand pane of the same data, but this time grouped by site address. You can still see the individual requests by expanding each individual site.

Select Sequence to see all events in a continuous list sorted by time. You’ll likely spend most of your time in this screen when debugging your own apps.

Charles merges the request and response into a single screen by default. However, I recommend you split them into separate events to provide greater clarity.

Click Charles ▸ Preferences and select the Viewers tab. Uncheck Combine request and response and press OK. You’ll need to restart Charles for the change to take effect.

Try poking around the user interface and looking at events. You’ll notice one peculiar thing: You can’t see most details for HTTPS events!

SSL/TLS encrypts sensitive request and response information. You might think this makes Charles pointless for all HTTPS events, right? Nope! Charles has a sneaky way of getting around encryption that you’ll learn soon.

More About Proxies

You may be wondering: “How does Charles do its magic?”

Charles is a proxy server, which means it sits between your app and computer’s network connections. When Charles automatically configured your network settings, it changed your network configuration to route all traffic through it. This allows Charles to inspect all network events to and from your computer.

Proxy servers are in a position of great power, but this also implies the potential for abuse. This is why SSL is so important: Data encryption prevents proxy servers and other middleware from eavesdropping on sensitive information.

In this case, however, you want Charles to snoop on your SSL messages to let you debug them.

SSL/TLS encrypts messages using certificates generated by trusted third parties called “certificate issuers.”

Charles can also generate its own self-signed certificate, which you can install on your Mac and iOS devices for SSL/TLS encryption. Since this certificate isn’t issued by a trusted certificate issuer, you’ll need to tell your devices to explicitly trust it. Once installed and trusted, Charles will be able to decrypt SSL events!

When hackers use middleware to snoop on network communications, it’s called a “man-in-the-middle” attack. In general, you DON’T want to trust just any random certificate or you could compromise your network security!

There are some cases where Charles’ sneaky man-in-the-middle strategy won’t work. For example, some apps use SSL pinning for extra security. SSL pinning means the app has a copy of the web server’s public key, and it uses this to verify network connections before communicating. Since Charles’ key wouldn’t match, the app would reject the communication.

In addition to logging events, you can also use Charles to modify data on the fly, record it for later review and even simulate bad network connections. Charles is really powerful!





Charles Proxy & Your iOS Device

Until recently the only way to proxy traffic from a physical iOS device through Charles Proxy was to tell your iOS device to send all network traffic to your computer. This is still a common practice and you’ll go through that next, but first you’re going to check out the new Charles Proxy for iOS app!

Open the App Store on your iOS device and search for Charles Proxy. Unfortunately there is no free version of their iOS app so you’ll have to purchase it if you want to follow along with this section.

Note: Don’t want to purchase the iOS app? Worry not! You can simply skip this section to the next one, where you’ll learn how to route your app’s network traffic to your Mac.

Install the app on your device and open it up. The initial screen shows the proxy is inactive with a switch as well as an overview of some key stats for any running session. Toggle the proxy on.

Once asked for permission to install VPN Configurations, tap Allow.

The status will change to Active.

Tap on the Current Session disclosure indicator arrow and the app will navigate to view comparable with the top pane on the desktop app. If you don’t see any requests, switch to Safari and load a web page.

Tap on any of the individual requests and you will drill down to a detail view for that request. As with the desktop app, any SLS/TLS encrypted traffic is still obfuscated. Time to fix that :]

Still within the Charles Proxy app, navigate back to the initial screen by tapping the back arrow at the top left of your screen twice. With the proxy still active, tap the Settings gear at the top left of the screen. Select SSL Proxying.

At the bottom of this screen are detailed instructions for installing and trusting the Charles Proxy CA Certificate. First, install the certificate using the button in the app. Your device will app switch to Safari and ask for permission to install the profile.

Note: If you have an Apple Watch paired with your device, it will ask whether to install the profile on the device or the watch. Choose iPhone.

Once the profile is installed, open Settings app. You should see a new Profile Downloaded option. Tap it and choose the Install option on the top-right.

You will be prompted for your device passcode, if you have one, followed by a confirmation screen warning you that this certificate is unverified. Tap Install again. Finally, an action screen will appear from the bottom of the screen with a final confirmation. Apple really wants to make sure you want to install this :]

Again, don’t install just any random certificate or else you may comprise your network security! At the end of this Charles Proxy tutorial, you’ll also remove this certificate.

You should get a confirmation screen that the profile is installed. Next, you need to trust that certificate. Still in the Settings app, navigate to General ▸ About ▸ Certificate Trust Settings. Find the Charles Proxy certificate and toggle the switch to on. A warning dialog will appear. Select Continue.

Switch back to the Charles Proxy app and the certificate status will now be labeled as Trusted. Toggle the Enabled switch at the top of the screen to on.

In Charles, navigate back to the main Settings page and save your changes. Open the current session and clear all the traffic using the broom icon in the bottom-left of the screen. Navigate to Safari and reload a web page. Then, navigate back to Charles Proxy. Tap on one of the requests and tap Enable SSL Proxying.

Go back to the current session and clear the session once again. Re-open Safari and reload the page a final time. Now, if you navigate back to Charles Proxy, the URL for which you enabled SSL Proxying will be marked with a blue network icon rather than a lock icon.

Tap the URL and you can now see the full detail of each request. Tap into a request for more detail.

Tap View body to view the full response body. Hurrah! :]

Even though this example used Safari, the following process will work when opening any app on your device, including your own, when wanting to debug your app’s networking.

Tap back to the request page and disable SSL Proxying. Tap back to the initial view and set the Charles Proxy status to Inactive to stop proxying traffic.

Proxying iOS Traffic Using Charles Proxy for MacOS

What happens if you want to inspect traffic on a Simulator, or don’t have the Charles Proxy iOS app? No problem! It’s simple to set up Charles to proxy traffic from any computer or device on your network, including your iOS devices.

Open Charles Proxy on your Mac and turn off macOS proxying by clicking Proxy (drop-down menu) ▸ macOS Proxy to uncheck it. This way, you’ll only see traffic from your iOS device.

Next, click Proxy ▸ Proxy Settings, click the Proxies tab and make note of the port number, which by default should be 8888.

Then, click Help ▸ Local IP Address and make note of your computer’s IP address. If there is more than one IP Address, pick en0.

Now, grab your iOS device. Open Settings, tap on Wi-Fi and verify you’re connected to the same network as your computer. Then, tap on the ⓘ button next to your Wi-Fi network. Scroll down to the HTTP Proxy section, select Configure Proxy and then tap Manual.

Enter your Mac’s IP address for Server and the Charles HTTP Proxy port number for Port. Tap Save.

Head back to the Charles MacOS app. Tap the Record/Pause button if not already recording traffic.

You should get a pop-up warning from Charles on your Mac asking to allow your iOS device to connect. Click Allow. If you don’t see this immediately, that’s okay. It may take a minute or two for it to show up.

You should now start to see activity from your device in Charles! But note, you can’t view SSL traffic just yet. As with the iOS app, you’ll need to install a certificate from Charles.

Still on your iOS device, open Safari and navigate to http://www.charlesproxy.com/getssl. On the pop-up, tap Allow.

Note: Again, if you have an Apple Watch paired with the device, iOS will prompt you to choose between your device and watch for installing the profile. Select iPhone.

In what should now be a familiar journey for you, switch to Settings and install the profile. Tap Install, enter your passcode (if set up) then tap Install again after the warning appears, and then tap Install one more time. Finally, tap Done.

As before, open the Settings app and navigate to General ▸ About ▸ Certificate Trust Settings. Trust the certificate that you have just installed.

Next, in the MacOS app select Proxy ▸ SSL Proxying Settings. Ensure Enable SSL Proxying is checked and add a value for the traffic you want to inspect.

Note: If you don’t know what values to put here, you can select a request in the app with the secondary (right) click and select Enable SSL Proxying from there.

You should now be able to see the full request and response bodies for that connection.

Snooping on Someone Else’s App

If you are like most developers, you’re curious about how things work. Charles enables this curiosity by giving you tools to inspect any app’s communication — even if it’s not your app.

Go to the App Store on your device and find and download Weather Underground. This free app is available in most countries. If it’s not available, or you want to try something else, feel free to use a different app.

You’ll notice a flurry of activity in Charles while you’re downloading Weather Underground. The App Store is pretty chatty!

Once the app is installed, launch the app and click the broom icon in Charles to clear recent activity.

Tap Search and enter the zip code 90210 and select Beverley Hills as your location in the app. Tap View. If you were to use your current location, the URL that the app fetches could change if your location changes, which might make some later steps in this Charles Proxy tutorial harder to follow.

There are tons of sites listed in the Structure tab! This is a list of all activity from your iOS device, not just the Weather Underground app.

Switch to the Sequence tab and enter wund in the filter box to show only weather traffic.

You should now see just a few requests to api.wunderground.com. Click one of them.

The Overview section shows some request details, but not much, as you haven’t enabled SSL proxying for api.wunderground.com yet.

Click on Proxy ▸ SSL Proxying Settings and click Add. Enter api.wunderground.com for the Host, leave the Port empty and click OK to dismiss the window.

Back in the Weather Underground app, pull down to refresh and re-fetch data. If the app doesn’t refresh, you might need to kill it from the multitasking view and try again.

Huzzah! Charles shows Unencrypted requests! Look for one of the requests with a URL containing forecast10day. This contains the payload that’s used to populate the weather screen.

Modifying the Response

Time to have some fun and change the data before the app gets it. Can you get the app to break or act funny?

In Charles, Right-click the request within the Sequence list, and click the Breakpoints item in the pop-up list. Now each time you make a request with this URL, Charles will pause and let you edit both the request and response.

Again on your device, pull down to refresh the app.

A new tab titled Breakpoints should pop up with the outgoing request. Simply click Execute without modifying anything. A moment later, the Breakpoints tab should again re-appear with the response.

Click on the Edit Response tab near the top. At the bottom, select JSON text. Scroll down and find temperature and change its value to something insane like 9800. Click Execute.

Note: If you take too long editing the request or response, the app may silently time out and never display anything. If the edited temperature doesn’t appear, try again a little quicker.

9800°F is crazy hot out! Seems like the app can’t show temperatures over 1000°. I guess we’ll never get forecasts for the surface of the sun. That’s a definite one-star rating. ;]

Back in Charles, delete the breakpoint you set by going to Proxy ▸ Breakpoint Settings.

Uncheck the entry for api.wunderground.com to temporarily disable it, or highlight the row and click Remove to delete it. Pull down to refresh, and the temperature should return to normal.

Simulating Slow Networking

Click the Tortoise icon to start throttling and simulate a slow network. Click Proxy ▸ Throttle Settings to see available options. The default is 56 kbps, which is pretty darn slow. You can also tweak settings here to simulate data loss, reliability issues and high latency.

Try refreshing the app, zooming the map and/or searching for another location. Painfully slow, right?

It’s a good idea to test your own app under poor network conditions. Imagine your users on a subway or entering an elevator. You don’t want your app to lose data, or worse, crash in these circumstances.

Apple’s Network Link Conditioner provides similar throttling capabilities, yet Charles allows for much finer control over network settings. For example, you can apply throttling to only specific URLs to simulate just your servers responding slowly instead of the entire connection.

Remember to turn off throttling when you’re done with it. There’s nothing worse than spending an hour debugging only to find you never turned off throttling!

Troubleshooting Your Own Apps

Charles Proxy is especially great for debugging and testing your own apps. For example, you can check server responses to ensure you have JSON keys defined correctly and expected data types are returned for all fields. You can even use throttling to simulate poor networks and verify your app’s timeout and error-handling logic.

You’ll try this out using an app called ModeratorsExplorer from Lorenzo Boaro’s excellent tutorial on UITableView Infinite Scrolling. Download the starter app using the Download Materials button at the top and bottom of this tutorial. Make sure Charles is recording, open the project in Xcode and build and run it on device or simulator.

This app takes the name of a Stack Exchange site and displays a list of all the moderators for that site. Type stackoverflow into the input box and tap Find Moderators!.

Oh no! What happened? It looks like there was an error decoding the data from the service. Let’s see if Charles can help us get to the bottom of the problem.

Switch to Charles Proxy (on your Mac) and in the sequence tab change the filter to api.stackexchange.com. Look at the response to one of the requests, you should see something like this:

{ "items": [{ "badge_counts": { "bronze": 2559, "silver": 2155, "gold": 195 }, "account_id": 11975, "is_employee": true, "last_modified_date": 1553115014, "last_access_date": 1553333702, "reputation_change_year": 14388, "reputation_change_quarter": 14388, "reputation_change_month": 3898, "reputation_change_week": 1090, "reputation_change_day": 20, "reputation": 791280, "creation_date": 1222667162, "user_type": "moderator", "user_id": 23354, "accept_rate": 100, "location": "Forest of Dean, United Kingdom", "website_url": "http://blog.marcgravell.com", "link": "https://stackoverflow.com/users/23354/marc-gravell", "profile_image": "https://i.stack.imgur.com/3vbu5.jpg?s=128&g=1", "display_name": "Marc Gravell" }, { "badge_counts": { "bronze": 2333, "silver": 2524, "gold": 141 }, ...

The response contains an array called items whose elements contain all the details about each moderator. Interesting.

In Xcode, open PagedModeratorResponse.swift in the Networking group. Notice anything fishy? The PagedModeratorsResponse is expecting an array of Moderators. But notice how there is no CodingKey set up for the array. This means the app is expecting the list of moderators to be called the default name — moderators in this case.

Replace the following:

case moderators

With:

case moderators = "items"

Build and run the app again. Type stackoverflow into the input and hit the button.

Success! This is a trivial but good demonstration of how viewing networking traffic in Charles Proxy can help you uncover bugs in your networking code.

Removing Charles’ Certificate

In the past, Charles created a shared certificate across everyone’s devices that used it. Fortunately, Charles now creates unique certificates. This significantly reduces the chance of a man-in-the-middle attack based on this certificate, but it’s still technically possible. Therefore, you should always remember to remove the Charles’ certificates when you’re done with it.

First, remove the certificate(s) from macOS. Open the Keychain Access application located in the folder Applications ▸ Utilities. In the search box, type in Charles Proxy and delete all the certificates that the search finds. There is most likely only one to delete. Close the application when you’re done.

Next, remove the certificates from your iOS device. Open the Settings app and navigate to General ▸ Profiles. Under Configuration Profiles, you should see one or more entries for Charles Proxy. Tap on one and then tap Remove Profile. Enter your passcode (if required) and confirm the removal. Repeat this for each Charles Proxy certificate.

Profiles & Device Management isn’t available in the iOS Simulator. To remove the Charles Proxy certificates, reset the simulator by clicking the Hardware menu and then Erase All Content and Settings….

You should also turn off the Proxy for your Wi-Fi connection on your iPhone by opening Settings and visiting Wi-Fi, tapping the ⓘ button, scrolling down to the HTTP Proxy section, selecting Configure Proxy and then tapping Off.

Where to Go From Here?

We hope you enjoyed this Charles Proxy tutorial! Charles Proxy has a ton more features that aren’t covered in this tutorial, and there are many more details for those that you learned today. Check out Charles’ website for more documentation. The more you use Charles, the more features you’ll discover.

You can download the fixed version of the project using the Download Materials button at the top or bottom of this tutorial.

You can also read more about SSL/TLS on Wikipedia at https://en.wikipedia.org/wiki/Transport_Layer_Security. Apple most likely will eventually require all apps to use secure network connections, so you should adopt this soon if you haven’t already.

Do you know of any other useful apps for debugging networking? Or do you have any debugging battle stories? Join the discussion below to share them!