<commit_before>import Foundation

extension ModelList
{
    private static let kJsonName:String = "cities"
    private static let kJsonExtension:String = "json"
    private static let kKeyId:String = "_id"
    private static let kKeyName:String = "name"
    private static let kKeyCountry:String = "country"
    private static let kKeyCoord:String = "coord"
    private static let kKeyLat:String = "lat"
    private static let kKeyLon:String = "lon"
    
    func loadItems(completion:@escaping(() -> ()))
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        { [weak self] in
            
            self?.dispatchLoadItems(completion:completion)
        }
    }
    
    //MARK: private
    
    private func dispatchLoadItems(completion:@escaping(() -> ()))
    {
        guard
            
            let resourceUrl:URL = Bundle.main.url(
                forResource:ModelList.kJsonName,
                withExtension:ModelList.kJsonExtension)
            
        else
        {
            return
        }
        
        let data:Data
        
        do
        {
            try data = Data(contentsOf:resourceUrl)
        }
        catch
        {
            return
        }
        
        let json:Any
        
        do
        {
            try json = JSONSerialization.jsonObject(
                with:data,
                options:
                JSONSerialization.ReadingOptions.allowFragments)
        }
        catch
        {
            return
        }
        
        loadItems(json:json, completion:completion)
    }
    
    private func loadItems(
        json:Any,
        completion:@escaping(() -> ()))
    {
        guard
        
            let jsonList:[AnyObject] = json as? [AnyObject]
        
        else
        {
            return
        }
        
        var items:[ModelListItem] = []
        
        for jsonItem:AnyObject in jsonList
        {
            guard
            
                let item:ModelListItem = loadItem(
                    json:jsonItem)
            <commit_msg>Expose methods for testing<commit_after>import Foundation

extension ModelList
{
    private static let kJsonName:String = "cities"
    private static let kJsonExtension:String = "json"
    private static let kKeyId:String = "_id"
    private static let kKeyName:String = "name"
    private static let kKeyCountry:String = "country"
    private static let kKeyCoord:String = "coord"
    private static let kKeyLat:String = "lat"
    private static let kKeyLon:String = "lon"
    
    class func factoryResourceUrl() -> URL?
    {
        let resourceUrl:URL? = Bundle.main.url(
            forResource:kJsonName,
            withExtension:kJsonExtension)
        
        return resourceUrl
    }
    
    func loadItems(completion:@escaping(() -> ()))
    {
        DispatchQueue.global(qos:DispatchQoS.QoSClass.background).async
        { [weak self] in
            
            self?.dispatchLoadItems(completion:completion)
        }
    }
    
    func loadData(url:URL) -> Data?
    {
        let data:Data
        
        do
        {
            try data = Data(contentsOf:url)
        }
        catch
        {
            return nil
        }
        
        return data
    }
    
    func loadJson(data:Data) -> Any?
    {
        let json:Any
        
        do
        {
            try json = JSONSerialization.jsonObject(
                with:data,
                options:
                JSONSerialization.ReadingOptions.allowFragments)
        }
        catch
        {
            return nil
        }
        
        return json
    }
    
    //MARK: private
    
    private func dispatchLoadItems(completion:@escaping(() -> ()))
    {
        guard
            
            let url:URL = ModelList.factoryResourceUrl(),
            let data:Data = loadData(url:url),
            let json:Any = loadJson(data:data)
            
        else
        {
            return
        }
        
        loadItems(json:json, completion:completion)
    }
    
    private func loadItems(
        json:Any,
        completion:@escaping(() -> ()))
    {
        guard
        
            let jsonList:[AnyObject] = json as? [AnyObject]
        
        else
        {
            return
        }
        
        var items:[ModelListItem] = []
        
        for jsonItem:AnyObject in jsonList
        {
            guard
            
                let item:ModelListItem = loadItem(
                    json:jsonItem)
            
{'fraction_non_alphanumeric': 0.07969094922737306, 'fraction_numerical': 0.0, 'mean_word_length': 1.3537662337662337, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5701093', 'n_tokens_mistral': 1229, 'n_tokens_neox': 1190, 'n_words': 248}