iOS plist преобразование в словарь

Задача

Нужно загрузить данные из plist файла, перевести эти данные в словарь (Dictionary) и использовать этот словарь для вывода таблицы. В итоге,  должно получится такое приложение:

Решение

Создание проекта

Надеюсь читататедь сможет собрать проект по моему описанию. Я не буду как в предыдущих статьях описывать каждый клик в среде разработки Xcode. Однако, если возникнут сложности, то можно прочитать предыдущие статьи на сайте в разделе «Уроки на реальных примерах».

Создайте проект в Xcode, используюя в качестве шаблона Single View Application. К имеющемуся ViewController добавьте из библиотеки объектов TableViewController. Main.storyboard должен выглядет как-то так:

Далее нужно добавить класс для работы с TableViewController, присвоить его контроллеру в Storyboard.   Также нужно добавить две кнопки и один Label из библиотеки элементов на ViewController. Разместите эти элементы и задайте нужные констрейнты:

 Добавьте outlet для Label и два action для кнопок.

Также выполните ctrl — перетягивание с кнопки «Показать таблицу» на DictTableViewController и укажите тип перехода Show.

Кликните на значке перехода и укажите имя для него, например  ShowTableSegue.

И последнее в нашей подготовке — кликните на ячейке внутри Table View Controller и задайте Reuse Identifier — например DictCell. Можно еще для удобства увеличить высоту ячейки в контроллере.

iOS Plist

Как было написано ранее plist — это просто определение термина. Он означает AnyObject​, который, является коллекцией объектов, которыми может быть ТОЛЬКО один из следующих типов: NSString, NSArray, NSDictionary, NSNumber, NSData, NSDate.

Другими словами, plist — это коллекция из определенных видов элементов, которые являются стандартными типами. Эта коллекция может содержать любое количество этих самых элементов.

В нашем примере мы будем использовать plist-файл, из которого мы загрузим данные и заснем их в таблицу. Опять же, для нашего примера мы создадим вручную нужный нам файл, но у вас файл скорее всего уже будет существовать, поэтому вы просто можете добавить его в проект и перейти к следующему пункту статьи.

Перейдите в меню File -> New -> File… Добавьте Property List и укажите имя для файла. Я указал MyData.plist

В Project Navigator нажмите правой кнопкой на только что созданном файле и выберите Open As -> Source Code.

Замените текст файла на следующий:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SamsungDevice</key>
	<array>
		<string>Galaxy Phone</string>
		<string>Edge Phone</string>
		<string>Galaxy Tab</string>
		<string>Gear Watch</string>
		<string>Low Cost Phones</string>
	</array>
	<key>AppleDevice</key>
	<array>
		<string>iPhone</string>
		<string>Mac</string>
		<string>iPad</string>
		<string>MacBookPro</string>
		<string>Apple Watch</string>
		<string>Apple TV</string>
	</array>
</dict>
</plist>

Думаю, по тексту понятно, что в этом файле у нас есть словарь с двумя элементами, каждый из которых это ключ типа строка и массив строк в качестве значений. Если открыть это же файл средством просмотра plist-файлов (правой кнопкой по файлу MyData.plist -> Open As -> Property List), то можно будет увидеть структуру нашего файла в общем и данные массивов AppleDevice и SamsungDevice в частности.

Работа с файлами

Для начала добавьте outlet для хранения загруженных данных.

//
// proSwift.ru
//
// Swift 3 


var dictFromFile = NSDictionary()

Далее в методе, который вызывается при нажатии на кнопку загрузки напишите следующий код:

//
// proSwift.ru
//
// Swift 3 

    @IBAction func loadData(_ sender: UIButton) {
        let path = Bundle.main.path(forResource: "MyData", ofType: "plist")
        if let path = path {
            dictFromFile = (NSDictionary(contentsOfFile: path))!
            
            dalaLabel.text = "Данные загружены"
        }
    }

Мы получаем путь для фала, который существует в нашей песочнице. Файловая система накладывает существенные ограничения на работу с файлами внутри проекта и полные ограничения для работы с файлами за пределами проекта. Поэтому нужно использовать методы такие как path(forResource:ofType:) для получения пути файла внутри песочницы.

Далее загруженные данные мы преобразуем в словарь методом класса NSDictionary (contentsOfFile:). После этих манипуляций мы меняем текст dalaLabel, чтобы отразить окончание процесса загрузки.

Вот и все. Две строки позволяют быстро загрузить данные из plist файла.

В итоге текст фалйа ViewController.swift у меня выглядит так:

//
//  proSwift.ru
//
//  ViewController.swift
//  PlistToTable
//
//  Created by Andrew Belozerov on 18.03.17.
//  Copyright © 2017 Andrew Belozerov. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    var dictFromFile = NSDictionary()
    
    @IBOutlet weak var dalaLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    @IBAction func loadData(_ sender: UIButton) {
        let path = Bundle.main.path(forResource: "MyData", ofType: "plist")
        if let path = path {
            dictFromFile = (NSDictionary(contentsOfFile: path))!
            
            dalaLabel.text = "Данные загружены"
        }
    }
    
    @IBAction func showTable(_ sender: UIButton) {
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

 

Загрузка данных в таблицу

Ну и последний шаг это загрузка дынных из файла в таблицу.

Еще раз подумаем что же нам надо и чем мы располагаем.

У нас есть словарь который содержит два колюча и и два соответствующих массива строк. А для отображения таблицы нам нужен массив элементов в качестве источника данных. Мы уже разбирали как преобразовать коллекцию словарей в источник данных для таблицы с секциями в этой статье. Давайте воспользуемся методом, который был описан раньше.

Весь дополнительный функционал и соответсвенно код мы будем писать в файле DictTableViewController.swift.

Сразу после строки import UIKit Добавьте следующий код вспомогательной структуры:

//
// proSwift.ru
//
// Swift 3 

struct Objects {  //Вспомогательная структура
    var sectionName : String!
    var sectionObjects : [String]!
}

А уже внутри класса добавьте свойство с массивом  объектов вспомогательной структуры. Следует также добавить свойство для получения и хранения данных из контролера с кнопками внутри класса с таблицей.

//
// proSwift.ru
//
// Swift 3 

    var dataSource = [Objects]() // массив объектов вспомогательных структур
    var loadedData = NSDictionary() // в это свойство мы передадим данные при переходе segue




Самое время скомпилировать проект и посмотреть что у нас получилось. У меня проект выглядит так:

После нажатия на кнопку загрузки выполняется преобразование данных из файла меняется текст на экране. А если нажать на кнопку перехода к таблице, то откроется таблица, но на текущий момент она пустая.

Теперь мы убедились, что переход от главного экрана с кнопками к таблице работает, но нам нужно передать данные, полученные из файла. Для этого внутрь класса основного контроллера — ViewController, — добавим метод, который выполняется при переходе.

//
// proSwift.ru
//
// Swift 3 

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ShowTableSegue" {
            let dtvc = segue.destination as! DictTableViewController
            dtvc.loadedData = dictFromFile
        }
    }

Как я писал ранее этот метод выполняется при любом переходе, но нам нужен именно наш переход ShowTableSegue, поэтому мы сначала проверили что это именно он, потом получили контроллер в который переходим, привели его к типу DictTableViewController и только после этого передали в этот контроллер в свойство для хранения данных наш загруженный plist-файл, преобразованный в словарь.

Следует проверить работоспособность нашего проекта. Для этого в метод viewDidLoad() класса DictTableViewController нужно добавить вывод в консоль содержимое свойства loadedData:

//
// proSwift.ru
//
// Swift 3

    override func viewDidLoad() {
        super.viewDidLoad()
        print(loadedData)
    }

После запуска проекта выполните загрузку данных и после появления сообщения о загрузке сделайте переход к таблице. Если все сделано верно, то в консоли появится следующий текст:

{
    AppleDevice =     (
        iPhone,
        Mac,
        iPad,
        MacBookPro,
        "Apple Watch",
        "Apple TV"
    );
    SamsungDevice =     (
        "Galaxy Phone",
        "Edge Phone",
        "Galaxy Tab",
        "Gear Watch",
        "Low Cost Phones"
    );
}

Теперь преобразуем полученные данные. Для этого добавим метод refractoring() в класс DictTableViewController.

//
// proSwift.ru
//
// Swift 3
    
    private func refractoring() {
        for (key , value)  in loadedData {
            dataSource.append(Objects(sectionName: key as! String, sectionObjects: value as! [String]))
        }
    }

Как видим это метод берет словарь в свойстве loadedData  и преобразует его массив источника данных dataSource. Нужно вызвать этот метод при загрузке контроллера, чтобы при его работе мы располагали всеми данными. Вставьте строку с вызовом метода refractoring() в метод viewDidLoad()

//
// proSwift.ru
//
// Swift 3

    override func viewDidLoad() {
        super.viewDidLoad()
        refractoring()
        print(loadedData)
    }

Чтобы не запутаться приведу код всего класса DictTableViewController на текущий момент:

//
// proSwift.ru
//
// Swift 3
//
//  DictTableViewController.swift
//  PlistToTable
//
//  Created by Andrew Belozerov on 18.03.17.
//  Copyright © 2017 Andrew Belozerov. All rights reserved.
//

import UIKit

struct Objects {  //Вспомогательная структура
    var sectionName : String!
    var sectionObjects : [String]!
}

class DictTableViewController: UITableViewController {

    var dataSource = [Objects]()
    var loadedData = NSDictionary()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        refractoring()
        print(loadedData)
    }
    
    private func refractoring() {
        for (key , value)  in loadedData {
            dataSource.append(Objects(sectionName: key as! String, sectionObjects: value as! [String]))
        }
    }
}

Работа с таблицей

Для начала измените стиль таблицы, чтобы отображались заголовки секций. В нашем примере это будет необходимо. Как вы наверное уже догадались у нас в таблице будет две секции: AppleDevice и SamsungDevice, а втабличных частях этих секций — будут устройства из массива данных.

Давайте добавим методы работы с таблицами, используя в качестве источника данных массив dataSource

//
// proSwift.ru
//
// Swift 3 

   // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return dataSource.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource[section].sectionObjects.count
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return dataSource[section].sectionName
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DictCell", for: indexPath)
        cell.textLabel?.text = dataSource[indexPath.section].sectionObjects[indexPath.row]
        return cell
    }

Итак, сначала мы получили количество секций таблицы, следующим методом мы будем возвращать количество ячеек в каждой секции. Как видите это количество будет равно количеству устройств в массиве данных. Третий метод возвращает текстовый заголовок для секций и четвертый непосредственно ячейку таблицы.

Опять же, я не останавливаюсь на особенностях работы с таблицами в iOS, примеров и уроков куча. Но если что-то не понятно — можно задавать вопросы в комментариях — я с удовольствием отвечу.

Запустите проект,  загрузите данные и выведите таблицу. У меня получилось вот так:

Вот и все. Мы загрузили данные из plist-файла, преобразовали данные для вывода в таблицу и вывели красивую структурированную таблицу на экран.

Полный код фалов

ViewController.swift:

//
//  proSwift.ru
//
//  Swift 3 
//
//  ViewController.swift
//  PlistToTable
//
//  Created by Andrew Belozerov on 18.03.17.
//  Copyright © 2017 Andrew Belozerov. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    var dictFromFile = NSDictionary()
    
    @IBOutlet weak var dalaLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    @IBAction func loadData(_ sender: UIButton) {
        let path = Bundle.main.path(forResource: "MyData", ofType: "plist")
        if let path = path {
            dictFromFile = (NSDictionary(contentsOfFile: path))!
            
            dalaLabel.text = "Данные загружены"
        }
    }
    
    @IBAction func showTable(_ sender: UIButton) {
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ShowTableSegue" {
            let dtvc = segue.destination as! DictTableViewController
            dtvc.loadedData = dictFromFile
        }
    }
}

DictTableViewController:

//
//  proSwift.ru
//
//  Swift 3 
//
//  DictTableViewController.swift
//  PlistToTable
//
//  Created by Andrew Belozerov on 18.03.17.
//  Copyright © 2017 Andrew Belozerov. All rights reserved.
//

import UIKit

struct Objects {  //Вспомогательная структура
    var sectionName : String!
    var sectionObjects : [String]!
}

class DictTableViewController: UITableViewController {

    var dataSource = [Objects]()
    var loadedData = NSDictionary()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        refractoring()
        print(loadedData)
    }
    
    private func refractoring() {
        for (key , value)  in loadedData {
            dataSource.append(Objects(sectionName: key as! String, sectionObjects: value as! [String]))
        }
    }
    
   // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return dataSource.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource[section].sectionObjects.count
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return dataSource[section].sectionName
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DictCell", for: indexPath)
        cell.textLabel?.text = dataSource[indexPath.section].sectionObjects[indexPath.row]
        return cell
    }
}




Ссылка проекта на GitHub

Метки: ,
2 комментария на “iOS plist преобразование в словарь
  1. d33play:

    Спасибо за ваш ресурс, спасибо за старания! Очень полезная информация для начинающих в Xcode.

  2. Павел:

    Спасибо! За труд, хороший урок!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.