skip to Main Content

I have built a JSON file that contains 14,000 airports and their names, timezones and other details. When appropriate I am making a data request to the JSON file and creating an array of Airports that I can search.

This runs, but is this the most efficient way to do this? It seems that building an array of 14,000 items every run will slow down the app. I want to query the database for an airport by code and return the other items associated with that object.

Thanks

Airport Object:

struct Airport: Codable {
    var id: String
    var name: String
    var city: String
    var country: String
    var code: String
    var icao: String
    var timeZoneName: String
    
}

Data request code..

 do {
                let decoder = JSONDecoder()
                let airports = try decoder.decode([Airport].self, from: data)
               
            
                
                if let i = airports.firstIndex(where: { $0.icao == "KORD" }) {
                    print(airports[i].timeZoneName)
                    print(airports[i].name)
                    
                }
                
            } catch {
                print(error)
            }

2

Answers


  1. As mentioned in the comment, you should build a database for this. I think, CoreData is perfect for this.
    On the first launch, you can load all your data into database, but later you only need to reach the db.

    Add CoreData to existing project

    • New File (Cmd+N or File->New or right click on the folder)
    • Select DataModel type, name it any (like CoreData, or your project name)
    • Open AppDelegate and first import CoreData and add the following code into AppDelegate:
    // MARK: - Core Data stack
    
        lazy var persistentContainer: NSPersistentContainer = {
            /*
             The persistent container for the application. This implementation
             creates and returns a container, having loaded the store for the
             application to it. This property is optional since there are legitimate
             error conditions that could cause the creation of the store to fail.
            */
            let container = NSPersistentContainer(name: "CoreData")
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                     
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                     */
                    fatalError("Unresolved error (error), (error.userInfo)")
                }
            })
            return container
        }()
    
        // MARK: - Core Data Saving support
    
        func saveContext () {
            let context = persistentContainer.viewContext
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    let nserror = error as NSError
                    fatalError("Unresolved error (nserror), (nserror.userInfo)")
                }
            }
        }
    

    Create a new Entity

    • Select your CoreData file
    • Add Entity, name it like Airports
    • Add attributes
      coredataEntity

    Write functions what you need
    First I would write a manager for this like

    struct CoreDataManager {
        private static let appDelegate = UIApplication.shared.delegate as! AppDelegate
        private static let persistentContainer = appDelegate.persistentContainer
    
    //... all the functions come here
    }
    
    • For saving, it’ better to user pagination
    static func saveAirports(airports: [Airport], completion: ((Bool) -> Void)? = nil) {
            let airportsPerPage = 1000
            let pages = (Float(airports.count) / Float(airportsPerPage)).rounded(.up)
            var i: Float = 0
            var airportList = airports
            let backgroundContext = persistentContainer.newBackgroundContext()
            
            while i < pages {
                for airport in airportList.prefix(usersOnPage) {
                    let airportRow = Users(context: backgroundContext)
                    airportRow.id = airport.id
                    airportRow.name = airport.name
                    airportRow.city = airport.city
                    airportRow.country = airport.country
                    airportRow.icao = airport.icao
                    airportRow.timeZoneName = airport.timeZoneName
                    airportRow.code = airport.code
                    backgroundContext.insert(airportRow)
                }
                if airportList.count >= airportsPerPage {
                    airportList.removeFirst(airportsPerPage)
                } else {
                    airportList.removeFirst(airportList.count)
                }
                i += 1
            }
            do {
                try backgroundContext.save()
                guard let completion = completion else { return }
                completion(true)
            } catch {
                print(error)
                guard let completion = completion else { return }
                completion(false)
            }
        }
    
    • Sample search query
    static func search(_ searchText: String) -> [Airport]? {
            let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Airports")
            let namePredicate = NSPredicate(format: "name CONTAINS[cd] %@", searchText)
            let idPredicate = NSPredicate(format: "id CONTAINS[cd] %@", searchText)
            let compoundPredicate = NSCompoundPredicate(type: .or, subpredicates: [namePredicate, idPredicate])
            request.predicate = compoundPredicate
            do {
                let result = try context.fetch(request)
                var airports = [Airport]()
                for airport in result as! [Airport] {
                    airports.append(Airport(id: airport.id,
                    name: airport.name, 
                    city: airport.city,
                    country: airport.country,
                    icao: airport.icao,
                    timeZoneName: airport.timeZoneName,
                    code: airport.code)
                }
                return airports
            } catch {
                print(error)
                return nil
            }
        }
    

    I hope this helps 🙂

    Login or Signup to reply.
  2. Did you actually measure it? 14,000 entries isn’t a lot. I remember writing an app that processed about 200,000 items on an iPhone 4s.

    So measure it first. It might be fast enough, and the you leave it as it is. If it is close but not quite as fast as you want: instead of an array with 14,000 dictionaries, change your JSON file once into one dictionary with 8 keys if etc, each containing an array of 14,000 strings. So when you parse the JSON files the keys are not parsed 14,000 times, and you just create the array of Airport.

    Make one function returning the array of airports. Start loading the data on s background thread asap. (All if you want to stay with JSON).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search