Background thread URL download in Swift 4

I was recently working on a project in swift, and was not able to quickly find a way to use a background thread to do a URL download in Swift 4. There were plenty of snippets and tutorials out there, but they were all for older versions of Swift. (BTW Swift, you have to stop it with these breaking changes.)

So here is what I cobbled together to download from the OpenAQ web site, which has an open API to provide air quality data.

The Swift class that does the downloading and processing of the data from the site is this class, which I called OpenAQ.swift:

//
//  OpenAQ.swift
//  openaq
//
//  Created by Brian Prescott on 11/16/17.
//  Copyright © 2017 Brian Prescott. All rights reserved.
//
 
import UIKit
 
class OpenAQResult: NSObject {
    var city: String = ""
    var count: Int = 0
    var country: String = ""
    var locations: Int = 0
}
 
protocol OpenAQDelegate: class {
    func didFinish(sender: Any)
    func didError(sender: Any)
}
 
class OpenAQ: NSObject {
 
    weak var delegate:OpenAQDelegate?
    var returnValue: [OpenAQResult] = []
 
    func readDataBackground() -> Void {
        let urlString = "https://api.openaq.org/v1/cities?limit=10000"
        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { (data, res, err) in
 
            guard let data = data else {
                return
            }
 
            do {
 
                let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
                let resultsArray = json["results"] as! NSArray
                var retval: [OpenAQResult] = []
                for x in resultsArray {
                    let dict = x as! NSDictionary
                    let res = OpenAQResult()
                    res.city = dict.value(forKey: "city") as! String
                    res.count = dict.value(forKey: "count") as! Int
                    res.country = dict.value(forKey: "country") as! String
                    res.locations = dict.value(forKey: "locations") as! Int
                    if (res.count >= 10000) {
                        retval.append(res)
                    }
                }
                self.returnValue = retval.sorted(by: { $0.count > $1.count })
                self.delegate?.didFinish(sender: self)
            } catch {
                print("An error occurred")
            }
 
        }.resume()
    }
 
}

So then to use this class, here is a template of what your main view controller (or wherever you want to use it) would look like:

class MasterViewController: UITableViewController {
 
    var detailViewController: DetailViewController? = nil
    var objects = [OpenAQResult]()
    var openAQ: OpenAQ? = nil
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
        openAQ = OpenAQ()
        openAQ?.delegate = self
        openAQ?.readDataBackground()
 
        // do the rest of your initialization
    }
 
    // the rest of your class would go here
 
}
 
extension MasterViewController: OpenAQDelegate {
    func didError(sender: Any) {
        print ("An error occurred!!!")
    }
 
    func didFinish(sender: Any) {
        objects = (sender as! OpenAQ).returnValue
        if (objects.count == 0) {
            let alert = UIAlertController(title: "Error", message: "The data could not be loaded. Please establish an internet connection and press the Refresh button above.", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
 
        DispatchQueue.main.async {
            // do something here
            // for example, reload a table view
        }
    }
}

In the code above, when the didFinish method is called, the objects instance variable is retrieved from the openAQ variable, and you can then do something with the data you just retrieved.

BTW, Happy Thanksgiving to all my U.S. readers. It has been a very trying year personally and all around, but we still have so much to be thankful for.

Plain ol’ XML

So let’s say you are working on an application in the .NET sphere of influence, and you have an object that you want to turn into XML. However, you shoot it through an XmlSerializer and you think you are done.

Ugh, what’s all that cruft in the XML stream?

Well there are some options you can play with to get rid of the extra stuff and just get plain ol’ XML. Here is a code snippet, where you will pass in your own object instead of the generic Order as shown below:

public String GetPlainOldXML(Order order)
{
    var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
    var settings = new XmlWriterSettings();
    settings.OmitXmlDeclaration = true;
 
    XmlSerializer xsSubmit = new XmlSerializer(typeof(Order));
    var xmlString = "";
    using (var sww = new StringWriter())
    {
        using (XmlWriter writer = XmlWriter.Create(sww, settings))
        {
            xsSubmit.Serialize(writer, order, emptyNamepsaces);
            xmlString = sww.ToString();
        }
    }
 
    return xmlString;
}

BTW, Happy Birthday to Leslie West of Mountain fame.

Chuwi Surbook review

As I said before in my GPD Pocket review, I don’t usually do reviews, but I need a posting for September. So, here is my Chuwi Surbook review.

I have always liked the form factor of the Microsoft Surface, but certainly not the price. When I saw this on Indiegogo, while the specs were not up to Surface levels, the price was certainly enticing, and it looked like they were far enough along that I didn’t have to worry about a lengthy wait for delivery. (My Fusion Guitar finally showed up, so now I’m looking at you, Jamstack.)

The durability and build quality of this device seem good, as it has withstood lugging back and forth to work for a month or so now without incident.

The screen is 12 inches in diagonal, and the resolution is very good, on par with the retina MacBook Pro computers.

This device does have a 1.1 GHz processor, so in terms of computing power, it is not the speediest, especially since it only has 6 GB of RAM. However, so far it has performed well for me on my light and medium duty tasks. I even installed Visual Studio 2017 Community on it, and I am able to do development work on a .NET MVC site that I have been futzing around with. There is a little delay when trying to run the application, but overall not bad.

The one big beef I have with this device is that the trackpad on the keyboard cover is very finicky, which led me to disable that mouse in the Device Manager, and then I use either a Bluetooth mouse or the included pen.

Kudos to Chuwi for adding a Micro SD card slot on this device.

Rating: 4 stars out of 5

Pros:

  • Solid build quality
  • Great value for the cost

Cons:

  • Keyboard case trackpad is very tempermental

BTW, I know I missed the actual date by a couple of days, but Happy 30th Birthday to Star Trek: The Next Generation.

Totality

Well it has been over a week now since the Great American Eclipse, and I must say that despite the relative difficulty in witnessing it, it was well worth it.

We decided to make a western U.S. trip out of the eclipse, so on the morning of Monday, August 21, we found ourselves stopping in the little town of Guernsey, Wyoming. With just over 2 minutes of totality, this seemed like a good compromise between duration of totality and traffic.

So we found some street parking at the West River Park, settled in with our eclipse glasses, and were blown away when the the town slipped into the moon’s umbra. It was quite the rush.

Leaving the area was not a total disaster either, as I suspect that choosing to avoid the totality center line was a good decision. Our two hour drive back to Cheyenne only took about three hours, which is a lot better than some of the traffic jam horror stories we heard about.

We are already looking forward to April 8, 2024.

BTW, can it really be 20 years ago that Princess Diana was lost in a car crash?

Keezel review

The Keezel is another interesting technology item that I backed on Indiegogo, and so after using it for a while both at home and on the road, I thought it might just be time for a Keezel review.

The Keezel device is a VPN device that allows you to secure your wi-fi communications. After you power it up, you connect to it and then use a web browser to go into the device’s administration panel. From there, you can set up what wireless access point and VPN you want to connect to. In addition, it can function as a spare battery pack as well, powering any mobile devices that charge through a standard USB port.

Right out of the box, I had some issues trying to get the Keezel registered with my Premium activation code that was included in the box. No matter what I did, it still did not think that it was registered. After a couple of emails with Keezel support, it was working just fine. I suspect that there is some kind of issue with the Keezel software or with their back end managing the subscriptions, and that they did something manually on their side that enabled it to connect up.

Once set up, the device worked great at home. On the road, though, it did not work quite as well, but even then I suspect that it had more to do with the quality of the wi-fi on the road I was trying to connect to, than a problem with the Keezel device or software. Toward the end of my trip, the device was knocked out of commission because the wi-fi log in page that I was trying to use would not come through to the user, they would just get an error message in the browser.

Rating: 4 stars out of 5

Pros:

  • Compact and light
  • Good battery life
  • Fast response from support

Cons:

  • Can be a bit touchy
  • Location of supported VPNs does not include all locations

BTW, Happy Birthday to Clint Hurdle, manager of the Pittsburgh Pirates.

Error 404 on web service with map route

If you are getting an error 404 on your web service with a map route decorator on your methods, you might want to check your IIS settings to make sure that it is configured correctly.

Apparently, your app pool must be set up in integrated mode as opposed to classic mode in order for the routing to work.

BTW, Happy 10th Anniversary to the iPhone.

GPD Pocket review

I don’t usually do reviews, but with certain products that I think are particularly interesting or innovative, I will make an exception. As a result, here is my GPD Pocket review.

GPD Pocket: 7.0′ UMPC-Laptop with Windows 10

I saw this campaign pop up on an email to me from Indiegogo in February of this year, and thought that the idea was intriguing. So I backed the project and received the device about 4 months later, which in terms of some Indiegogo campaigns, is practically overnight. (I’m looking at you, Fusion Guitar.)

For an inexpensive device, I do like the build quality. The case is an alloy case that feels very solid and light, which hopefully translates to durability in the long run.

The screen is a 7 inch screen at a resolution of 1920×1200, and from what I have seen so far, is very good for most traditional uses. This device is not advertised as a gaming machine, so I have not tried to do anything crazy complicated with it yet.

Atom processors are notoriously sluggish, but the one in this device seems snappy enough, although the caveat again here is that I have not tried running anything challenging. I suspect that the 8 gigabytes of RAM configuration that I opted for is helping out on this front quite a bit.

On such a small device, the keyboard is always going to be a problem, and the GPD Pocket is no exception here. The biggest problem I have been having so far is that the number keys are off to the right by about one key, or in other words, on a normal keyboard, I go for the 3 key with my E finger, but on GPD Pocket, the 2 is right above E. As a result, I have to stop myself when going to the numbers row.

The one thing that I wish this device had was a MicroSD card slot to expand the device beyond the 128 gigabytes of storage that it comes with on the internal drive.

For the price that I paid (about $400 as an early adopter), I think it is a great device that I will make a lot of use of. That said, if the retail price goes up significantly from there, I am not sure if it has the same appeal.

Rating: 4 stars out of 5

Pros:

  • Compact and light
  • Good battery life
  • Good build quality

Cons:

  • Hard to touch type
  • No MicroSD card slot to expand storage

BTW, a posthumous Happy Birthday to Bob Keeshan.

They say it’s your birthday…

No technology related news or views today, just wanted to wish Joe Bonamassa a Happy 40th Birthday!

Watch out for #ifdef TARGET_IPHONE_SIMULATOR

I just wanted to take a moment before the sands of time in the April hourglass run out for a friendly reminder for those iOS developers who are working in older Objective-C code bases. Watch out for #ifdef TARGET_IPHONE_SIMULATOR, as at some point, a newer version of the iOS SDK broke this check. In the current TargetConditionals.h file, TARGET_IPHONE_SIMULATOR resolves to either a 0 or a 1 (eventually, see below), which means that no matter where you run the code, TARGET_IPHONE_SIMULATOR will be defined, hence the code inside the #ifdef will always run.

The solution is of course to use the following line instead:

#if TARGET_IPHONE_SIMULATOR

Keep in mind that TARGET_IPHONE_SIMULATOR itself is marked as deprecated in the iOS 10 SDK TargetConditionals.h file, so I suppose to be future-proof, you should instead use:

#if TARGET_OS_SIMULATOR

BTW, Happy Honesty Day to my U.S. readers. All you need to know about Honesty Day is that I think it is one of the best days on the calendar.

macOS VPN adventures

I use a VPN to connect for work, and have been having some macOS VPN adventures with my MacBook Pro. Mostly my problem was that, on occasion, the internet connectivity would not work, as I could connect to the VPN just fine, but any internet requests would just return with a failure.

The way I found to work around this issue was to go into the advanced settings for the VPN and hard code the DNS server of 8.8.8.8 in there. After doing that, it seemed to work much more solidly.

As a bonus tip, if you happen to use VMware Fusion on your Mac to run a Windows VM, I would also recommend going into the network adapter settings for your VM (all of them if you have more than one) by launching your VM and selecting Settings from the Virtual Machine menu, and select the Internet Sharing “Share with my Mac” setting instead of going directly to your Wi-Fi or Autodetect under Bridged Networking, or otherwise your VM will not communicate through the VPN you have running on your Mac at the same time.