how to implement json in realm swift - swift

I have a dictionary in json format. I want to show it with a table in my app(about 1000 cells) and also save it to realm database. I am new to database can anyone please tell me how to implement this? Should I try to convert the format outside the app or load the json when it is used?
My dictionary looks like this..
[
{
"id":0,
"book":1,
"lesson":1,
"kanji":"\u4e2d\u56fd\u4eba"
} {
"id":1,
"book":1,
"lesson":1,
"kanji":"\u65e5\u672c\u4eba"
},
...
]

First of all, you will need to install 2 pods:
pod 'SwiftyJSON' #great for handling JSON
pod 'RealmSwift' #Realm database
You will need to create the object that will be able to be saved in Realm. I suppose your object is some type of course or something similar. You can rename it per your needs:
import Foundation
import RealmSwift
import SwiftyJSON
class Course: Object {
#objc dynamic var id = 0
#objc dynamic var book = 0
#objc dynamic var lesson = 0
#objc dynamic var kanji = ""
override static func primaryKey() -> String? { //you need this in case you will want to update this object in Realm
return "id"
}
convenience init(courseJson: JSON) {
self.id = courseJson["id"].intValue
self.book = courseJson["book"].intValue
self.lesson = courseJson["lesson"].intValue
self.kanji = courseJson["kanji"].stringValue
}
}
Now, at the place where you get your json from the server, you need to do this:
var courses = [Course]()
let jsonArray = JSON(dataFromServer).arrayValue
for courseJson in jsonArray {
let course = Course(courseJson: courseJson)
courses.append(course)
}
//Now, you need to save these objects in Realm:
let realm = try! Realm()
try! realm.write {
realm.add(courses, update: true)
}
In the viewDidLoad method of the view controller where you want to show the data, you need to fetch all your Course objects from Realm, add them to an array and show data in the tableView/collectionView:
import UIKit
import RealmSwift
class PresentingCoursesVC: UIViewController {
//MARK: IBOutlets
#IBOutlet weak var tableView: UITableView!
//MARK: Properties
var courses = Results<Course>?
//MARK: Lifecycles
override func viewDidLoad() {
super.viewDidLoad()
getCourses()
}
//MARK: Private functions
private func getCourses() {
let realm = try! Realm()
courses = realm.objects(Course.self).sorted(byKeyPath: "id", ascending: true)
tableView.reloadData()
}
}
//MARK: TableView Datasource
extension PresentingCoursesVC: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return courses?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell") as! MyCustomCell
let course = courses[indexPath.row]
cell.setupCell(course: course)
return cell
}
}
I hope my answer was helpful!

class Book: Object {
#objc dynamic var id = 0
#objc dynamic var book = 0
#objc dynamic var lesson = 0
#objc dynamic var kanji = ""
}
class MyDic: Object {
var yourKeyOfDic = List<Book>()
}
Now map your dic with MyDic class object and then add that object in realm by following code,
let realm = try! Realm()
try! realm.write {
realm.add(yourDicObject)
}

Related

use func to delete a specific item from core data binary data

I am trying to delete binary data from core data. I am using a var int 'place' to determine what specific item I am trying to delete I am getting a runtime error under helpImage.shareInstance.deleteInfo(info: place) which is below.
Cannot convert value of type 'Int' to expected argument type 'Info'
What can I do to delete the 1st item saved in a core data binary attribute?
import UIKit;import CoreData
class ViewController: UIViewController {
var place = 0
override func viewDidLoad() {
super.viewDidLoad()
let gwen = UIImage(named: "unnamed.jpg")
if let imageData = gwen.self?.pngData() {
helpImage.shareInstance.saveImage(data: imageData)
}
let alz = UIImage(named: "alba.jpeg")
if let imageData = alz.self?.pngData() {
helpImage.shareInstance.saveImage(data: imageData)
}
}
#objc func deleteM(){
helpImage.shareInstance.deleteInfo(info: place)
}
}
class helpImage: UIViewController{
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static let shareInstance = helpImage()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveImage(data: Data) {
let imageInstance = Info(context: context)
imageInstance.img = data
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
func deleteInfo(info: Info) {
do {
try context.delete(info)
} catch {
print(error.localizedDescription)
}
}
}
I'm also a newbie in swift and learning so if anyone has feedback, I'm more than happy to implement.
here is stroryBoard: on "Save" button click, we will save images in CoreData and on "Show" button click, we will display a tableView with our images fetched from CoreData
here is coreData: don't forget to check Manual/None in Codegen in coreData Class
then manually add coreData NSManagedObject SubClass files (there will be two files).
To generate the class and properties files initially:
source: https://developer.apple.com/documentation/coredata/modeling_data/generating_code
From the Xcode menu bar, choose Editor > Create NSManagedObject Subclass.
Select your data model, then the appropriate entity, and choose where to save the files. Xcode places both a class and a properties file into your project.
// this is how your "Picture+CoreDataClass.swift" looks like
import Foundation
import CoreData
#objc(Picture)
public class Picture: NSManagedObject {
}
// this how your "Picture+CoreDataProperties.swift" looks like
import Foundation
import CoreData
extension Picture {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Picture> {
return NSFetchRequest<Picture>(entityName: "Picture")
}
#NSManaged public var pic: String?
#NSManaged public var id: Int64
}
extension Picture : Identifiable {
}
I have used below extension to get currentTimeStamp as we will need unique ID for each of our coreData object, we will pass currentTimeStamp as ID to be unique.
// MARK: - Get the Current Local Time and Date Timestamp
source: https://stackoverflow.com/questions/46376823/ios-swift-get-the-current-local-time-and-date-timestamp
extension Date {
static var currentTimeStamp: Int64{
return Int64(Date().timeIntervalSince1970 * 1000)
}
}
I have made CRUD functions in DataBaseHelper.swift file
// this "DataBaseHelper" Class
import Foundation
import UIKit
import CoreData
class DataBaseHelper {
// MARK: - Get Context
class func getContext() -> NSManagedObjectContext{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static let shareInstance = DataBaseHelper()
let context = getContext()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Picture")
// MARK: - Save Images
func saveImage(picString: String, id: Int64 = Date.currentTimeStamp) {
// here I have passed id = Date.currentTimeStamp, as we need unique ID for each of our object in coreData and Date.currentTimeStamp will always give unique integer. we will use this ID to update and delete particular object.
let entity = NSEntityDescription.entity(forEntityName: "Picture", in: context)!
let image = NSManagedObject(entity: entity, insertInto: context)
image.setValue(picString, forKey: "pic") // key should be same as the attributes taken in coreData Table.
image.setValue(id, forKey: "id") // key should be same as the attributes taken in coreData Table.
do {
try context.save()
print("Images saved in coreData")
print("imageString: \(picString), id: \(id)") // this will print your entered (saved) object in coreData
} catch let error {
print("Could not save images: \(error.localizedDescription)")
}
}
// MARK: - fetch Images
func fetchImages() -> [Picture] {
var arrImages = [Picture]()
fetchRequest.returnsObjectsAsFaults = false
do {
arrImages = try context.fetch(fetchRequest) as? [Picture] ?? [Picture]()
print("Images while fetching from coreData: \(arrImages)") // this will print all objects saved in coreData in an array form.
} catch let error {
print("Could not fetch images: \(error.localizedDescription)")
}
return arrImages
}
// MARK: - fetch Images by ID
func fetchImagesByID(id: Int64) -> Picture {
fetchRequest.returnsObjectsAsFaults = false
let predicate = NSPredicate(format: "id == \(id)")
fetchRequest.predicate = predicate
let result = try? context.fetch(fetchRequest)
return result?.first as? Picture ?? Picture()
}
// MARK: - Update Image
func updateImage(object: Picture) {
let image = fetchImagesByID(id: object.id) // we will first fetch object by its ID then update it.
image.pic = object.pic
do {
try context.save()
print("Image updated in CoreData")
print("after updating Picture --> \(object)")
} catch let error {
print("Could not update Picture: \(error.localizedDescription)")
}
}
// MARK: - Delete Image
func deleteImage(id: Int64) {
let image = fetchImagesByID(id: id) // we will first fetch object by its ID then delete it.
context.delete(image)
do {
try context.save()
print("Image deleted from CoreData")
} catch let error {
print("Could not delete Image --> \(error.localizedDescription)")
}
}
}
here is our ViewController:
I have added 4 images in Assets folder.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var btnSaveImages: UIButton!
#IBOutlet weak var tableViewPicture: UITableView!
#IBOutlet weak var btnShowImages: UIButton!
var resultImages = [Picture]() // this an an instance of our model class (coreData)
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "CoreData Demo"
setUpTableView()
}
// MARK: - Save Images
func saveImagesInCoreData() {
// I have added 4 images in Assets folder, here we will save 3 images in CoreData
let image1 = "flower1"
let image2 = "flower2"
let image3 = "flower3"
DataBaseHelper.shareInstance.saveImage(picString: image1)
DataBaseHelper.shareInstance.saveImage(picString: image2)
DataBaseHelper.shareInstance.saveImage(picString: image3)
}
// MARK: - Fetch Images
func fetchImagesFromCoreData() {
resultImages = DataBaseHelper.shareInstance.fetchImages() // this will give fetched images
}
// MARK: - Set Up TableView
func setUpTableView () {
tableViewPicture.delegate = self
tableViewPicture.dataSource = self
}
// MARK: - Button Save Images Event
#IBAction func btnSaveImages_Event(_ sender: UIButton) {
saveImagesInCoreData() // save images in CoreData
}
// MARK: - Button Show Images Event
#IBAction func btnShowImages_Event(_ sender: UIButton) {
fetchImagesFromCoreData() // fetch Images from CoreData
self.tableViewPicture.reloadData() // reload tableView
}
}
// MARK: - Extesnion TableViewDelegate and TableViewDataSource
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resultImages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell") ?? UITableViewCell()
cell.imageView?.image = UIImage(named: resultImages[indexPath.row].pic ?? "")
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let editAction = UITableViewRowAction(style: .default, title: "Edit") { (action, indexPath) in
print("Action Edit")
// here we will edit image of selected row in our tableView, we will update selected row with new image that is "flower4".
let image4 = "flower4"
self.resultImages[indexPath.row].pic = image4
DataBaseHelper.shareInstance.updateImage(object: self.resultImages[indexPath.row]) // update image of selected row in tableView
self.tableViewPicture.reloadData()
}
editAction.backgroundColor = .lightGray
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
print("Action Delete")
// here we will delete an object of selected row in our tableView.
DataBaseHelper.shareInstance.deleteImage(id: self.resultImages[indexPath.row].id) // delete object of selected row in tableView
self.resultImages.remove(at: indexPath.row) // remove from resultImages array
self.tableViewPicture.reloadData()
}
return [deleteAction, editAction]
}
}
if you have doubt feel free to ask !

Deleting Objects with subclasses in Swift Realm

I have a really simple database in Swift Realm for a todo app:
Items and their parent Categories.
The user can delete both the Items and the Categories with a simple swipe action. The action works fine, there are no issues when deleting Items. If I delete a Category, that works too, but I can still see the Items in the Realm Browser, those remain in the database even though there are no parent anymore. Obviously the user can't see these, they are doing nothing but still, it would be better to get rid of these with the parent Category. Are there any simple ways to do this?
class Category: Object{
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
#objc dynamic var order = 0
let items = List<Item>()
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDCat() -> Int {
let realm = try! Realm()
return (realm.objects(Category.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
class Item: Object {
#objc dynamic var title: String = ""
#objc dynamic var done: Bool = false
#objc dynamic var dateCreated: Date?
#objc dynamic var order = 0
var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDItem() -> Int {
let realm = try! Realm()
return (realm.objects(Item.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
override func updateModel(at indexPath: IndexPath) {
if let categoryForDeletion = self.categories?[indexPath.row] {
do {
try self.realm.write {
self.realm.delete(categoryForDeletion)
}
} catch {
print("Error deleting category, \(error)")
}
}
tableView.reloadData()
}
You just delete items first.
self.realm.delete(categoryForDeletion.items)
self.realm.delete(categoryForDeletion)
Or, with this extension, you can do this.
self.realm.delete(categoryForDeletion, cascading: true)

Realm Array and child relationships

I am trying to make a List in relation to my realm array. I don't know if it is possible to take a hard coded realm array and give each string its own list. Currently I have my array in a table view and when a row is selected it segues to its own viewController. I am trying to get each selected row to contain its own list. Here's the code
Data Model 1
import Foundation
import RealmSwift
class DateChange: Object {
#objc dynamic var itemId : String = UUID().uuidString
override static func primaryKey() -> String? {
return "itemId"
}
let dates = List<String>()
let selection = List<Home>()
convenience init(tag: String) {
self.init()
}
}
Data Model 2
class Home: Object {
#objc dynamic var itemId : String = UUID().uuidString
override static func primaryKey() -> String? {
return "itemId"
}
var parentCategory = LinkingObjects(fromType: Home.self, property: "selection")
View Controller 1
class WeekOfViewController: NSViewController {
let post = DateChange(tag: "")
post.dates.append("December 30th - January 5th")
post.dates.append("January 13th - January 19th")
}
func numberOfRows(in tableView: NSTableView) -> Int {
return 2
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "dateCell") , owner: self) as! NSTableCellView?
cell?.textField?.stringValue = post.dates[row]
return cell
}
func tableViewSelectionDidChange(_ notification: Notification) {
self.performSegue(withIdentifier: "selectedDate", sender: self)
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
// Unwrap the segue's identifier.
guard let identifier = segue.identifier else { return }
// Make sure this is the segue we care about.
if identifier == "selectedDate" {
let secondVC = segue.destinationController as! ViewController2
// Figure out which row was selected.
if let selectedRow = dateTableView?.selectedRow {
secondVC.selectedDate = post.dates[selectedRow]
}
View Controller 2
class ViewController2: NSViewController {
#IBAction func saveData(_ sender: NSButton) {
if let appendDate = selectedDate {
do {
try realm?.write {
let homeData = Home()
homeData.done = false
appendDate.dates.append()
}
} catch {
print("There was an error saving")
}
}
}
Yes. You can make multiple dimensional arrays.
Example:
1. Year object has a list of months
2. Month object has a list of days
3. Day object has a list of hours
4. etc, etc
When you app launches, you create a loop which initializes a Year, then initializes lots of months and appends them into the Year.Month_Array. Do the same for days_array in each month.
To save in Realm, call:
try! realm.write {
realm.add(Year)
}
Now you can read out you multi-level Realm object anytime you wish.
If my answer isn't clear, please let me know
The exact use case is a bit unclear but it seems that the app is a Master->Detail configuration where the master page contains a list of dates and then when a date is tapped, it segues to a detail page with further info.
Here's an example of code to handle the objects. You know how to populate a tableView with it's delegate methods so I'm omitting that part.
Suppose we want a list of events on the master page and then the activities of each event on the detail page. Start with two managed Realm objects; the event object which has a start and end date, the event title and the activities list within that event. Then there's the activity object.
class EventClass: Object {
#objc dynamic var start_date: Date?
#objc dynamic var end_date: Date?
#objc dynamic var event_title = ""
let activities = List<ActivityClass>()
}
class ActivityClass: Object {
#objc dynamic var title = ""
}
write an event with some activities to Realm
let formatter = DateFormatter()
formatter.dateFormat = "yyyymmdd"
let e0 = EventClass()
e0.event_title = "Workshop"
e0.start_date = formatter.date(from: "20180113")
e0.end_date = formatter.date(from: "20180119")
let a0e0 = ActivityClass()
a0e0.title = "Some activity"
let a0e1 = ActivityClass()
a0e1.title = "Another activity"
let a0e2 = ActivityClass()
a0e2.title = "Activity continues"
e0.activities.append(a0e0)
e0.activities.append(a0e1)
e0.activities.append(a0e2)
// write event 0 (e0) to realm which will create the event and activities
We are assuming both the master and detail views have tableViews, so load the events into the master tableView dataSource which is a Realm results class - it behaves like an array.
class ViewController: NSViewController {
var eventResults: Results<EventClass>? = nil
and then whenever it's time to populate the dataSource:
self.eventResults = realm.objects(EventClass.self)
and then the tableView delegate methods can get each row for display. It would look something like this on the master page
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier:
NSUserInterfaceItemIdentifier(rawValue: "dateCell") , owner: self) as! NSTableCellView?
//cell?.textField?.stringValue = post.dates[row]
let event = self.eventResults[row]
cell.textField?.stringValue = "Event: \(title) from: \(start) - \(end)"
return cell
}
so then the tableView would look something like this
Event: Workshop from: 2018-12-30 - 2018-01-05
Event: Workshop from: 2018-01-13 - 2018-01-19
etc
when a row is tapped, get that event object from the master tableView datasource, self.eventResults
let tappedEvent = self.eventResults[tappedRowIndex]
and then get the activity list
let activityList = tappedEvent.activities
that activityList can be passed via the segue (or 1000 other ways) to the detail viewController and it becomes the dataSource for the detail tableView. The detail page follows the same pattern except the dataSource is the activities.
Does that help?

Fatal Error: Index out of range - Swift 4 and Firebase

I am creating an iOS application using XCode 8 and Swift 4 that keeps track of events. I am using Firebase as my database tool for this app. I am running into an "index out of range" error in the code below. I am using a for loop to read the Event start and end times for each event in the Firebase Database. The code is able to return the event names, but not the times underneath. Why is this the case? Is there something wrong with my code, or is there something wrong with my database structure? The database structure used is pasted below the code here.
Code here:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class Event_List_Controller: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var eventList = [String]()
var startTimeArray = [String]()
var endTimeArray = [String]()
var ref: DatabaseReference?
var handle:DatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
endTimeArray.removeAll()
tableView.delegate = self
tableView.dataSource = self
let emailfinal = UserDefaults.standard.string(forKey: "splicedEmailStandard")
ref = Database.database().reference()
handle = ref?.child(emailfinal!).child("Event Data").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String {
self.eventList.append(item)
self.tableView.reloadData()
}
})
// FOR LOOP THAT POPULATES THE START AND END TIMES
for individualEvents in eventList {
ref = Database.database().reference()
handle = ref?.child(emailfinal!).child("Event Data").child(individualEvents).child("Start Time").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String {
self.startTimeArray.append("\(item)")
}
})
handle = ref?.child(emailfinal!).child("Event Data").child(individualEvents).child("End Time").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String {
self.endTimeArray.append("\(item)")
}
})
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "customClassCell") as! CustomTableViewCell
cell.classLabel.text = eventList[indexPath.row]
// FATAL ERROR: INDEX OUT OF RANGE HERE.
cell.timeLabel.text = "\(startTimeArray[indexPath.row]) - \(endTimeArray[indexPath.row])"
return cell
}
}
Database Structure:
email#email (User)
-> User Information
----> (Information)
---> Event Information
------> Event Name
----------> Event Start Time
----------> Event End Time
----------> Event Location
email#email (User 2)
----> ...
It looks like you're calling reloadData() before you're actually getting the start and end times for the events, so the startTimeArray and endTimeArray will not have objects at the index of the event you just got from firebase.
Also consider that with Firebase you're setting up observing rather than getting all of your data at once. Perhaps some simple class like Event:
class Event {
var name: String
var startTime: String
var endTime: String
init(name: String) {
self.name = name
self.startTime = "" //or some other sensible default before loading
self.endTime = "" //or some other default
}
}
Then have an array of Event object that your tableview is using instead of the array of strings, and update the Event objects and refresh the table view whenever you get new data in from Firebase

Passing Multiple Objects To WatchKit In A NSUserDefaults

With the help of some great tutorials and users here, I've had success implementing SwiftyJSON in my app and getting a basic WatchKit app built alongside. My last hurdle to pass is getting my whole set of parsed JSON data to be passed to WatchKit, as to allow me to choose from a cell in a TableView and pull up more specific detail on a piece of criteria.
I'm parsing JSON data in my Minion.swift file, like so;
import UIKit
class Minion {
var name: String?
var age: String?
var height: String?
var weight: String?
class func fetchMinionData() -> [Minion] {
let dataURL = NSURL(string: "http://myurl/json/")
var dataError: NSError?
let data = NSData(contentsOfURL: dataURL!, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &dataError)
let minionJSON = JSONValue(data)
var minions = [Minion]()
for minionDictionary in minionJSON {
minions.append(Minion(minionDetails: minionDictionary))
}
return minions
}
init(minionDetails: JSONValue) {
name = minionDetails["san"].string
age = minionDetails["age"].string
height = minionDetails["height"].string
weight = minionDetails["free"].string
}
}
For my iOS app, this is working well to populate my UITableView and subsequent Detail View. I have my ViewController.Swift like so;
import UIKit
class ViewController: UITableViewController {
let minions: [Minion]
required init(coder aDecoder: NSCoder!) {
minions = Minion.fetchMinionData()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let defaults = NSUserDefaults(suiteName: "group.com.mygroup.data")
let key = "dashboardData"
defaults?.setObject(minions, forKey: key)
defaults?.synchronize()
}
// MARK: Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
I've truncated much of the code as I don't believe it's relevant to WatchKit. In the WatchKit extension, I have my InterfaceController.swift like so;
import WatchKit
class InterfaceController: WKInterfaceController {
#IBOutlet weak var minionTable: WKInterfaceTable!
let defaults = NSUserDefaults(suiteName: "group.com.mygroup.data")
var dashboardData: String? {
defaults?.synchronize()
return defaults?.stringForKey("dashboardData")
}
let minions = ???
When I run the iOS app, it throws me the error "Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')" because I am passing the whole set of JSON data as "minions." If I set my NSUserDefaults key to "minions[0].name" it will pass the single string, but passing the whole set of data so the WatchKit table can allow me to choose a row seems to be evading me.
In advance, as always, I am most grateful.
Your Minion class need to implement the NSCoding. Then in your view controller you need to transfer your Minion object to NSData object.
class Minion: NSObject, NSCoding {
.....
init(coder aDecoder: NSCoder!) {
aCoder.encodeObject(name, forKey: "name")
aCoder.encodeObject(age, forKey: "age")
aCoder.encodeObject(height, forKey: "height")
aCoder.encodeObject(weight, forKey: "weight")
}
func encodeWithCoder(aCoder: NSCoder) {
name = aDecoder.decodeObjectForKey("name") as String
age = aDecoder.decodeObjectForKey("age") as String
height = aDecoder.decodeObjectForKey("height") as String
weight = aDecoder.decodeObjectForKey("weight") as String
}
}
In your ViewController class:
NSKeyedArchiver.setClassName("Minion", forClass: Minion.self)
defaults?.setObject(NSKeyedArchiver.archivedDataWithRootObject(minions), forKey: "minions")
If you want to retrieve the data from NSUserDefaults:
if let data = defaults?.objectForKey("minions") as? NSData {
NSKeyedUnarchiver.setClass(Minion.self, forClassName: "Minion")
let minions = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [Minion]
}