Skip to content →

diego.org Posts

Charles Proxy on iOS Simulator

Charles “is an HTTP proxy / HTTP monitor / Reverse Proxy that enables a developer to view all of the HTTP and SSL / HTTPS traffic between their machine and the Internet. This includes requests, responses and the HTTP headers (which contain the cookies and caching information).”

It is one of my favorite tools for debugging network applications. It supports Mac, Windows, Linux and even iOS.

The instructions on their website for SSL connections from within iPhone applications are not quite complete. Especially the last step which is tricky and I often forget. Here are complete instructions for SSL proxy connections on the iOS Simulator.

1. Add SSL Location. After launching Charles, open the menu:
Proxy > SSL Proxying Settings

The click the “Add Button”

Charles - Add Domain

Type in the location you want to add. You can use wildcards if you like. When you’re done, you should see you location in the list:

Charles - SSL Proxying Settings

2. Install Charles Root Certificate in iOS Simulators. Open the menu:

Help > Install Charles Root Certificate in iOS Simulators

Charles - Install Root Cert iOS

This will install the Charles Root Certificate on the Simulator.

3. Enable the Root Certificate. This is the step, they forget to document. And the last part of it is tricky.

On the Simulator, go to:

Settings > General > About > Certificate Trust Settings

Charles - iOS Trust Settings

Swipe the switch to enable the certificate. It will give a warning which you must choose “Continue”

Charles - iOS Trust Settings Enable

You’ll be placed back on the Certificate Trust Settings page with the switch on:

Charles - iOS Trust Settings Enabled

Now is the critical part. Quit the Simulator. Now run your application again and Charles will be able to decrypt your network communications.

Comments closed

Covert GPX to Strava Route

Strava has a labs feature to convert a GPX file to a route: Strava GPX to Route.

But it doesn’t work for me:
Strava GPX to Route Error

And I’m not the only one:
Error computing Route when loading a GPX file

Here is a workaround for converting a GPX file to Strava Route:

1. Export the GPX file.

2. Go to GPSies and import your GPX file:

GPSies Import

Verify it:

GPSies Verify

It adds a timing of 10 mph. You can change this, but it doesn’t matter.

3. Export the GPX Track file.

GPSies Export

4. Go Strava and Upload Activity.

Strava Upload

Click Choose File and select the GPX file exported from GPSies. This will create a new ride.

5. Save the activity as a route.

Strava Create Route

Strava Route Verify

Strava Name Route

6. Delete the activity.

Strava Delete

Comments closed

iOS Swipe Keyboard to dismiss

In iOS 7, Apple added an option to UIScrollView which could allow you to dismiss the keyboard when dragged.

UIScrollViewKeyboardDismissMode

public enum UIScrollViewKeyboardDismissMode : Int {

    case none

    case onDrag // dismisses the keyboard when a drag begins

    case interactive // the keyboard follows the dragging touch off screen, and may be pulled upward again to cancel the dismiss
}

This is very easy to do. Just set the keyboardDismissMode property to .onDrag or interactive:

let tableView = UITableView(frame: .zero, style: .plain)
tableView.keyboardDismissMode = .interactive

You should set this on all tableviews that contain text fields.

For example code, see: https://github.com/dougdiego/iOSDemos

Comments closed

Vapor Error – root manifest not found

I got this error when trying to build a Vapor project.

$ vapor build
No .build folder, fetch may take a while...
Fetching Dependencies [ • ]
Error: backgroundExecute(code: 1, error: "warning: \'fetch\' command is deprecated; use \'resolve\' instead\nerror: root manifest not found\n", output: "")

I recently experienced a lot of error with getting Vapor 3.0 to run during the beta and Swift 4.1. I assumed the error had to do with that. Had I read closer I might have figured it out. I was not in the build directory. Duh! After changing to the build directory everything worked fine.

If you end up on this page, make sure you are in the build directory when trying to build a Vapor project.

Comments closed

Programmatically selecting a row in a UITableView

I had a need to programmatically select a row in a UITableView. In order to do this, you have to do two things:

 
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
self.tableView.delegate?.tableView!(self.tableView, didSelectRowAt: indexPath)

selectRow highlights the selected row and didSelectRowAt will call didSelectRowAt if you need to perform whatever action you defined.

For example code, see: https://github.com/dougdiego/iOSDemos

Comments closed

macOS Word Breaks

macOS has a feature that lets you change the word delimiter.  By default it considers the “.” to be part of the word and selects the whole thing.  But you can change it to break on the period.

This feature used to be called “Word Break” in previous macOS release.  In macOS Sierra it can be found by going to “System Preferences” and selecting “Language & Region”.  Here you’ll see an option for “Region”.  If you’re in the United States, it should be set to “United States”.  You can change it to “United States (Computer)”.

Here’s what’s happening: ASCII special characters that are part of words between alphabetic ASCII characters:

  • '.:_ in Standard
  • '_ in English (United States, Computer)

 

Here is an example of “United States”

 

 

Here is an example of “United States (Computer)”

 

 

If you change the word break setting, you have to quit and reopen applications to apply the changes.

Note: I found this tip a few years ago and used it on my Mac. After getting a new Mac, it took me awhile to figure out how to do this again. This post is a reminder to myself for next time I need to remember how to do this.

Comments closed

Fastlane solution for Crashlytics – Missing dSYMs

While working on a new Xcode project, I was unable to get my crash logs on Crashlytics . In the Crashlytics dashboard, I would see the following error:

"Found 1 unsymbolicated crash from missing dSYMs in 1 version in the last 24 hours"

Crashlytics even had a page dedicated to this: All about Missing dSYMs.

In my case the issue was with bitcode being enabled, which it is by default on all new iOS Projects. When bitcode is enabled, Apple recompiles your project on their server. Then you need to download the dSYMS to your computer and upload them to Crashlytics. This is a manual process that takes a lot of time. I was dreading doing this a second time after figuring out the process.

I figured this is something that fastlane might be able to help me with. Sure enough, they’ve come up with a lane to automate this. You can read more about it here:

Automatically download and upload dSYM symbolication files from iTunes Connect for Bitcode iOS apps using fastlane

Simply add this to your Fastfile:

lane :refresh_dsyms do
  download_dsyms                  # Download dSYM files from iTC
  upload_symbols_to_crashlytics   # Upload them to Crashlytics
  clean_build_artifacts           # Delete the local dSYM files
end

Then run it like this:

fastlane refresh_dsyms

Thanks fastlane!

Comments closed

DispatchGroup with Swift

Sometimes you have a need to wait on several asynchronous tasks before doing something in your code. DispatchGroup is a powerful API that lets you group together these tasks into one task.

DispatchGroup allows for aggregate synchronization of work. You can use them to submit multiple different work items and track when they all complete, even though they might run on different queues. This behavior can be helpful when progress can’t be made until all of the specified tasks are complete.

For example you have a ViewController that shows data from three different APIs. You need data from all 3 API’s before you can render the page. One way to do this would be to chain network calls and render the page after the 3rd API completed. But this leads to ugly nest code and requires that the APIs be called synchronously, rather than asynchronously.

Let’s looks look at how to solve this problem by chaining first.

I’ll be using httpbin.org for these examples. It’s a great tool for testing HTTP requests and responses.

This URL allows you to make an HTTP request that will wait a duration in seconds. For example, this will take 10 seconds to load:
https://httpbin.org/range/1024?duration=10

I’m using this API so you can easily see the time difference.

I wrote a simple method called makeNetworkRequest(), which take a duration in seconds and uses NSURLSession to call httpbin.org:

    func makeNetworkRequest(duration:Int, completion: @escaping (_ result: String?) -> Void) {
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
        let url = URL(string: "https://httpbin.org/range/1024?duration=\(Int(duration))")!
        NSLog("makeNetworkRequest() \(url)")
        let task = session.dataTask(with: url) { (data, response, error) in
            if error != nil {
                NSLog(error!.localizedDescription)
                completion(nil)
            } else {
                if let str = String(data: data!, encoding: String.Encoding.utf8) {
                    completion(str)
                } else {
                    completion(nil)
                }

            }
        }
        task.resume()
    }

 

Here is an example using nesting:

makeNetworkRequest(duration: 2) { (str) in
	NSLog("Request #1 \(str ?? "nil"))\n")

	makeNetworkRequest(duration: 3) { (str) in
		NSLog("Request #2 \(str ?? "nil"))\n")

		makeNetworkRequest(duration: 10) { (str) in
			NSLog("Request #3 \(str ?? "nil"))\n")
			
			NSLog("Done!")
		}
	}
}

As you can see this accomplishes a solution to our problem, but there is a lot of nested code and called the API’s synchronously. It took 15 seconds to wait for all 3 responses.

Now let’s try this with DispatchGroup. With DistpatchGroup you first create a DistpachGroup:

let group = DispatchGroup()

For each block of code you want to execute you call:

group.enter()

Before the API and then

Group.leave()

When it finishes executing.

Finially group.notifiy(…) will be called when all group.enter() calls have a successful group.leave() response:

group.notify(queue: DispatchQueue.global(qos: .background)) {
NSLog(“All 3 network reqeusts completed”)
completion(strings)
}

Here’s a complete example:

func makeNetworkRequests(completion: @escaping (_ result: String?) -> Void) {
        let group = DispatchGroup()
        var strings = "start"
        
        group.enter()
        makeNetworkRequest(duration: 2) { (str) in
            NSLog("Request #1 \(str ?? "nil"))\n")
            if let str = str {
                strings = strings + str
            }
            group.leave()
        }
        
        group.enter()
        makeNetworkRequest(duration: 3) { (str) in
            NSLog("Request #2 \(str ?? "nil"))\n")
            if let str = str {
                strings = strings + str
            }
            group.leave()
        }
        
        group.enter()
        makeNetworkRequest(duration: 10) { (str) in
            NSLog("Request #3 \(str ?? "nil"))\n")
            if let str = str {
                strings = strings + str
            }
            group.leave()
        }
        
        group.notify(queue: DispatchQueue.global(qos: .background)) {
            NSLog("All 3 network reqeusts completed")
            completion(strings)
        }
    }

Notice how much easier this code is to read. No more nested blocks. It’s also asynchronous. It only takes 10 seconds for all 3 to return, because we’re only waiting on the longest API to respond.

For example code, see: https://github.com/dougdiego/iOSDemos

Comments closed

Cocoapods – Unable to find a specification for

When trying to install CodableAlamofire, I kept getting this error

[!] Unable to find a specification for `CodableAlamofire` depended upon by…

I check and the pod existed in the CocoaPods Specs.  I also noticed it said it was updated (in this case added) 2 days ago:

The problem was that my CocoaPods was out of date.  I ran this command to update it:

pod repo update master

After updating and running pod install again, I was able to install it.

Installing CodableAlamofire (1.0.0)
Comments closed

D.L. Bliss Camping Tips

One of our favorite campsites is D.L. Bliss State Park. After camping there many times, here are a few tips we have learned over the years.

Map not to Scale. When you arrive and check in, the ranger will give you a map of the campsite. There are 3 main loops at Bliss and the beach. The map shows this. Make sure you note the text that says “Not to scale”. The loops are not close to each other. Our first time at Bliss we stayed at the top loop. We looked at the map and saw the beach was just below the 2 lower loops. We decided it would be a quick walk. We packed out gear and started walking down. Needless to say, it was not a quick walk. It’s walkable, just be aware.

Bear Boxes. All food and any scented item must go in the bear box (not your car). This includes soaps, lotions, gum, etc. There are bears. We’ve seen them. They are not as aggressive as in Yosemite, but do exist. Make sure all your stuff will fit in the bear box. The inside dimensions of the food lockers 36″ deep, 43″ wide, and 22″ high. Make sure your cooler(s) fits. And if you’re sharing, make sure whoever is sharing stuff will all fit together in 1 bear box.  Note: if you have a Yeti cooler or other bear-proof cooler, you can leave it outside the bear box as long as you have locks on it.

Ice. Tahoe gets warm during the summer. The metal bear boxes heat up in the sun and get really hot. The ice in your cooler melts quickly. Luckily the campsite host sells ice. When it’s hot, we refresh it daily.  Sometime the camp host will drive around in their ATV’s and sell ice and firewood. Fresh ice is also good if you want to plan on making cocktails.

Water, Bathroom and Showers. Each loop has a bathroom and showers. The showers take coins instead of quarters. You must buy these coins from the ranger kiosk when you enter the park. There are water spigots at about every couple of campsites. We drink the water from here. It’s drinkable and tastes good.

Beach. One of our favorite parts of D.L. Bliss is the beach. There is a small beach with a parking lot. One side of the beach is houses and the other side is rocks. On a busy day the beach will fill up fast. People come early and setup camp to reserve a spot on the beach. It’s best to go down around 7am and setup a spot on the beach. It’s best to setup a sunshade and some towels. If you’re early enough get one of the handful of picnic benches. There is a parking lot at the beach too. This also fills up quickly. On a busy day the lot is full by 8 or 9am. Another idea is to take your breakfast to the beach. You can setup on the beach and then make breakfast there or at the picnic tables by the parking lot.

Paddle Boards and Kayaks. Bliss is a great spot for Paddle Boards and Kayaks. There is a rocky cove just to the right of the beach. It’s fun spot to paddle around. The rocks are a popular spot for jumping into the water. It’s also popular with boats, so be careful.

Yellow Jackets. Towards the end of the camping season, the yellow jackets can be bad. One year (2016) was so bad we almost went home. The ranger suggested yellow jacket traps. We tried that and it didn’t do much. We found it best to just try and ignore them. They didn’t bite, but were just annoying.

Hiking. There are several good hikes around Bliss. The closest is the Rubicon Trail. It starts by the beach parking lot and is a 8.3 mile round trip to Emerald Bay. The gift shop at Emerald Bay sells popsicles and other packaged ice cream, which is a nice treat for the kids. Eagle Lake is another favorite hiking spot. The trailhead starts at the top of Emerald Bay. It’s a 1.8 mile round trip hike. There is a parking lot and you can park on the street, but during a busy weekend it fill early. I suggest being there by at least 8am. You start off by hiking next to a waterfall, then you cross it and then take a trail to Eagle Lake.  If you want to go further, there are lots of trails into Desolation Wilderness.

Cell Phone Reception.  There is some Cell Phone Reception at the lower campsites and beach.

South Lake Tahoe. It’s only 13 miles to South Lake Tahoe if you need to run out and get supplies.  With no traffic, this only takes about 25 minutes.  But on a busy weekend this could take over an hour.  The traffic gets bad because of people going to the beaches and Camp Richardson.  If you need to get out, I suggest doing it before 10am.

 

Comments closed