I'm using ESRI iOS SDK 10.2.5,my app was built using swift 2.3 and i have converted it to 3.2 now,everythng works fine.
but there's a little problem in displaying the markers in feature layer which used to work correctly in swift 2.3
the method signature have slightly changed after migration .
here's the method signature for 2.3 (works fine):
func featureLayer(featureLayer: AGSFeatureLayer!, operation op: NSOperation!, didQueryFeaturesWithFeatureSet featureSet: AGSFeatureSet!) {
if (featureLayer == self.featureLayer) {
// featureset has more than 1 element
for poi in featureSet.features {
var attr = [String: AnyObject]()
attr["NAME"] = poi.allAttributes()[LanguageController.localizedString("poi_detail_name")] as? String;
attr["DETAIL"] = poi.allAttributes()["PHONE_NUMBER"] as? String;
attr["CATEGORY_EN"] = poi.allAttributes()["FACILITY_CAT_EN"] as? String;
attr["IS_OFFICE"] = false;
attr["IS_HDKP"] = false
let graphic = AGSGraphic(
geometry: poi.geometry,
symbol: getSymbolForGraphic(poi as! AGSGraphic),
attributes: attr)
self.mapLayer.addGraphic(graphic);
}
SVProgressHUD.dismiss()
}
method signature for swift 3.2 :
func featureLayer(_ featureLayer: AGSFeatureLayer!, operation op: Operation!, didQueryFeaturesWith featureSet: AGSFeatureSet!) {
if (featureLayer == self.featureLayer) {
// problem here,featureset.feature has 0 elements
for poi in featureSet.features {
var attr = [String: Any]()
attr["NAME"] = (poi as AnyObject).allAttributes()[LanguageController.localizedString("poi_detail_name")] as? String;
attr["DETAIL"] = (poi as AnyObject).allAttributes()["PHONE_NUMBER"] as? String;
attr["CATEGORY_EN"] = (poi as AnyObject).allAttributes()["FACILITY_CAT_EN"] as? String;
attr["IS_OFFICE"] = false as Any
attr["IS_HDKP"] = false as Any
let graphic = AGSGraphic(
geometry: (poi as AnyObject).geometry,
symbol: getSymbolForGraphic(poi as! AGSGraphic),
attributes: attr)
self.mapLayer.addGraphic(graphic);
}
SVProgressHUD.dismiss()
}
Model class for manipulating features :
class POICategory: NSObject, NSCoding
{
var isActive : Bool;
var name : String;
var nameFilter : String;
var key : String;
init(isActive: Bool,name : String, nameFilter: String, key: String){
self.isActive = isActive;
self.name = name;
self.nameFilter = nameFilter;
self.key = key;
}
required init?(coder aDecoder: NSCoder) {
self.isActive = aDecoder.decodeObject(forKey: "isActive") as? Bool
self.name = aDecoder.decodeObject(forKey: "name") as! String
self.nameFilter = aDecoder.decodeObject(forKey: "nameFilter") as! String
self.key = aDecoder.decodeObject(forKey: "key") as! String
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.isActive, forKey: "isActive")
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.nameFilter, forKey: "nameFilter")
aCoder.encode(self.key, forKey: "key")
}
}
i have debugged the code and came to know that the featureSet.feature has no elements in swift 3.2 but has elements in 2.3,that is why it is not entering the loop and setting the graphics to the marker
why this happens,since this method is from esri's delegate..is this bug from their end ?
please help me in fixing this issue,if anyone has any idea on this
there's nothing wrong in migration from 2.3 to 3.2,only thing which might break according to me is the code in your model class
try changing your model class in the second init from
self.isActive = aDecoder.decodeObject(forKey: "isActive") as? Bool
to
self.isActive = aDecoder.decodeBool(forKey: "isActive")
you are trying to read a boolean value like object, which might be the issue
Related
I already read several posts and tried every solution, nothing works in my case.
I am using this complex data structure and need to store array of DrawnObjects in file. But it crashing when it first came to encode a variable which itself is an array of Structure type. Any help ?
[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x1702668c0
enum ArchiverKeys : String
{
case imageView = "ImageView"
case stateArray = "StateArray"
case bezierPathArray = "BezierPathArray"
case reactangleArray = "ReactangleArray"
case deleted = "Deleted"
}
struct RectanglePath {
var point1: CGPoint
var point2: CGPoint
var point3: CGPoint
var point4: CGPoint
}
struct StateObject {
var isAdd = true
var path = String()
}
class DrawObject: NSObject , NSCoding {
var ImageView = UIImageView()
var arrStates = [StateObject]()
var arrBezierPaths = [UIBezierPath]()
var rects = [RectanglePath]()
var deleted = false
override init() {
ImageView = UIImageView()
arrStates = []
arrBezierPaths = []
rects = []
deleted = false
}
func encode(with archiver: NSCoder) {
archiver.encode(self.ImageView, forKey:ArchiverKeys.imageView.rawValue )
archiver.encode(self.arrStates, forKey: ArchiverKeys.stateArray.rawValue)
archiver.encode(self.arrBezierPaths, forKey:ArchiverKeys.bezierPathArray.rawValue )
archiver.encode(self.rects, forKey: ArchiverKeys.reactangleArray.rawValue)
archiver.encode(self.deleted, forKey: ArchiverKeys.deleted.rawValue)
}
required convenience init(coder unarchiver: NSCoder) {
self.init()
self.ImageView = unarchiver.decodeObject(forKey: ArchiverKeys.imageView.rawValue) as! UIImageView
self.arrStates = unarchiver.decodeObject(forKey: ArchiverKeys.stateArray.rawValue) as! [StateObject]
self.arrBezierPaths = unarchiver.decodeObject(forKey: ArchiverKeys.bezierPathArray.rawValue) as! [UIBezierPath]
self.rects = unarchiver.decodeObject(forKey: ArchiverKeys.reactangleArray.rawValue) as! [RectanglePath]
self.deleted = (unarchiver.decodeObject(forKey: ArchiverKeys.deleted.rawValue) != nil)
}
}
func saveArrayTo(_ directoryName: String , arrayToSave: NSArray) {
// let encodedData = NSKeyedArchiver.archivedData(withRootObject: arrayToSave)
NSKeyedArchiver.archiveRootObject(arrayToSave, toFile: directoryName)
}
func loadArrayFrom(_ directoryName: String ) -> NSArray? {
let result = NSKeyedUnarchiver.unarchiveObject(withFile: directoryName)
return result as? NSArray
}
You cannot encode a Swift struct out of the box, you have to add a computed property and an init method to make the struct property list compliant
struct StateObject {
var isAdd = true
var path = ""
init(propertyList : [String:Any]) {
self.isAdd = propertyList["isAdd"] as! Bool
self.path = propertyList["path"] as! String
}
var propertyListRepresentation : [String:Any] {
return ["isAdd" : isAdd, "path" : path]
}
}
Now you can archive the array
archiver.encode(self.arrStates.map{$0.propertyListRepresentation}, forKey: ArchiverKeys.stateArray.rawValue)
and unarchive it
let states = unarchiver.decodeObject(forKey: ArchiverKeys.stateArray.rawValue) as! [[String:Any]]
self.arrStates = states.map { StateObject(propertyList: $0) }
Alternatively leave StateObject unchanged and
in encode(with replace the line
archiver.encode(self.arrStates, forKey: ArchiverKeys.stateArray.rawValue)
with
let arrStatesAsPlist = arrStates.map { return ["isAdd" : $0.isAdd, "path" : $0.path] }
archiver.encode(arrStatesAsPlist, forKey:ArchiverKeys.stateArray.rawValue)
in init(coder replace the line
archiver.encode(self.arrStates, forKey: ArchiverKeys.stateArray.rawValue)
with
let arrStatesAsPlist = unarchiver.decodeObject(forKey: ArchiverKeys.stateArray.rawValue) as! [[String:Any]]
arrStates = arrStatesAsPlist.map { StateObject(isAdd: $0["isAdd"] as! Bool, path: $0["path"] as! String) }
Notes:
Since you are assigning default values to all properties you can delete the entire init() method and the init() call in init(coder.
Don't use NSArray in Swift. Use a native Array
It's not a good idea to archive an UI element like UIImageView. Archive the image data.
I am trying to add a key and a value to a dictionary then add this dictionary the user defaults and read back into a dictionary object. I have two questions that I would really appreciate any help in,
1) why is the dictionary being read from user defaults empty? Since I added a key and a value to the dictionary shouldn't those be saved to the dictionary I retrieve from user defaults?
let defaults = UserDefaults.standard;
var myDict = [String: String]()
myDict["key"] = "value"
defaults.setValue(myDict, forKey: "myDict")
let mydict2 = defaults.object(forKey: "myDict") as? [String: String] ?? [String:String]()
print(mydict2)
2) What can I do to this code if the dictionary stores a custom class that I created as a value or a key so if the dictionary was like this:
class Car {
var engineSize: Int
var color: String
init() {
engineSize = 2000
color = "blue"
}
}
class Boat {
var surfaceArea: Int
var weight: Int
init() {
surfaceArea = 3500
weight = 4000
}
}
var myDict = [Car: Boat]()
how can I save that second dict to user defaults and read it from there?
Thank you
EDIT:
This is the answer suggested by ebby94:
var myDict = [String:String]()
myDict["key"] = "value";
let data = NSKeyedArchiver.archivedData(withRootObject: myDict)
UserDefaults.standard.set(data, forKey: "myDict")
func foo()
{
guard let archivedData = UserDefaults.standard.value(forKey: "myDict") as? Data
else
{
print("failed1")
return
}
guard var unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: archivedData) as? [String:String]
else
{
print("failed2")
return
}
print(unarchivedDictionary["key"]!)
}
foo()
However this prints failed1, I'm assuming the data wasn't archived correctly. Can this be because I'm running it in playground?
If you want to save custom object to userDefault first you need to encode & decode variable then save using archive & get data using unarchive.
class Car {
var engineSize: Int
var color: String
init() {
engineSize = 2000
color = "blue"
}
// Decode
required convenience public init(coder decoder: NSCoder)
{
self.init()
if let engineSize = decoder.decodeObject(forKey: "engineSize") as? Int
{
self.engineSize = engineSize
}
if let color = decoder.decodeObject(forKey: "color") as? String
{
self.color = color
}
}
// Encode
func encodeWithCoder(coder : NSCoder)
{
if let engineSize = self.engineSize
{
coder.encode(engineSize, forKey: "engineSize")
}
if let color = self.color
{
coder.encode(color, forKey: "weight")
}
}
}
class Boat {
var surfaceArea: Int
var weight: Int
init() {
surfaceArea = 3500
weight = 4000
}
// Decode
required convenience public init(coder decoder: NSCoder)
{
self.init()
if let surfaceArea = decoder.decodeObject(forKey: "surfaceArea") as? Int
{
self.surfaceArea = surfaceArea
}
if let weight = decoder.decodeObject(forKey: "weight") as? Int
{
self.weight = weight
}
}
// Encode
func encodeWithCoder(coder : NSCoder)
{
if let surfaceArea = self.surfaceArea
{
coder.encode(surfaceArea, forKey: "surfaceArea")
}
if let weight = self.weight
{
coder.encode(weight, forKey: "weight")
}
}
You can't save a dictionary directly in UserDefaults. You'll have to convert the dictionary into data and save it and then retrieve the data and unarchive it into dictionary.
Archive and save to UserDefaults
let myDict = [String:String]()
let data = NSKeyedArchiver.archivedData(withRootObject: myDict)
UserDefaults.standard.set(data, forKey: "myDict")
Retrieve and unarchive the data to dictionary
guard let archivedData = UserDefaults.standard.value(forKey: "myDict") as? Data
else{return}
guard let unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: archivedData) as? [String:String]
else{return}
Userdefaults not work in Playground. you need to implement and start it in an App in Simulator
I have been researching all over the interweb, especially Stack overflow, and my own references I have at home, but I have not been able to figure out what is wrong with my code. I have spent most of my day trying to figure this issue out, and I hope somebody here can help point me in the correct direction.
Setup:
I have a chess app that I am building in Swift 3.0, and the structure is as follows: BoardModel is the class that holds all of the data about the game, Piece is a class within board model which holds data about itself within the BoardModel, Piece also has a PieceType enum for .knight, .king, etc.
The BoardModel has a 2D array of Piece representing the chess board. Now, with each move, I want to save the game data in Game Center, but before I can even try to store the game data, the encoding throws an error and that is where I am. The error just points at AppDelegate with the statement: "Thread: 1 signal SIGABRT".
Here is the code that is the problem along with the Piece class:
let pieceData = NSKeyedArchiver.archivedData(withRootObject: board.board[0][0]) // where the error is thrown
class Piece: NSObject, NSCoding {
var isSelected: Bool
var type: PieceType
var isWhite: Bool
var isFirstMove: Bool
var symbol: String!
var position: (row: Int, col: Int)!
override init() {
isSelected = false
type = PieceType.empty
isWhite = true
isFirstMove = true
}
required init(coder aDecoder: NSCoder) {
isSelected = aDecoder.decodeBool(forKey: "isSelected")
type = aDecoder.decodeObject(forKey: "type") as! BoardModel.PieceType
isWhite = aDecoder.decodeBool(forKey: "isWhite")
isFirstMove = aDecoder.decodeBool(forKey: "isFirstMove")
symbol = aDecoder.decodeObject(forKey: "symbol") as! String
position = aDecoder.decodeObject(forKey: "position") as! (Int, Int)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(isSelected, forKey: "isSelected")
aCoder.encode(type, forKey: "type")
aCoder.encode(isWhite, forKey: "isWhite")
aCoder.encode(isFirstMove, forKey: "isFirstMove")
aCoder.encode(symbol, forKey: "symbol")
aCoder.encode(position, forKey: "position")
}
init(isSelected: Bool, type: PieceType, isWhite: Bool, isFirstMove: Bool, symbol: String, position: (Int, Int)) {
self.isSelected = isSelected
self.type = type
self.isWhite = isWhite
self.isFirstMove = isFirstMove
self.symbol = symbol
self.position = position
}
func setPosition(to newPosition: (row: Int, col: Int)) {
position = newPosition
}
}
if your Enum is like this PieceType and type is Int
enum PieceType : Int {
case empty
case notEmpty
}
Then write encode and decode method like this way
required init(coder aDecoder: NSCoder) {
isSelected = aDecoder.decodeBool(forKey: "isSelected")
type = PieceType(rawValue: aDecoder.decodeObject(forKey: "type") as! Int)!
isWhite = aDecoder.decodeBool(forKey: "isWhite")
isFirstMove = aDecoder.decodeBool(forKey: "isFirstMove")
symbol = aDecoder.decodeObject(forKey: "symbol") as! String
position = aDecoder.decodeObject(forKey: "position") as! (Int, Int)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(isSelected, forKey: "isSelected")
aCoder.encode(type.rawValue, forKey: "type")
aCoder.encode(isWhite, forKey: "isWhite")
aCoder.encode(isFirstMove, forKey: "isFirstMove")
aCoder.encode(symbol, forKey: "symbol")
aCoder.encode(position.row, forKey: "position.row")
aCoder.encode(position.col, forKey: "position.col")
}
i have check bellow code and it work's
let pice = Piece(isSelected: true, type: .empty, isWhite: true, isFirstMove: true, symbol: "Test", position: (2, 10))
let pieceData = NSKeyedArchiver.archivedData(withRootObject:pice)
print(pieceData)
And Out put is
386 bytes
So I've just converted a small app from Swift 2.2 to Swift 3. I've gotten rid of the usual errors and bits of mop up required after the auto converter but I've got a run time issue that I can't work out.
I've got a custom class that I am saving to NSUserDefaults with the NSCoding protocol. When I try to decode the encoded object from NSUserDefaults it fails on the guard let duration = decoder.decodeObject(forKey: "duration") as? Int line as duration is printing as nil. Decoding the title string works fine however so at least that line of the encode function is working correctly.
This worked fine in 2.2 and I can't find anything indicating that Swift 3 made changes to the NSCoding. Any help would be much appreciated.
class TimerModel: NSObject, NSCoding, AVAudioPlayerDelegate {
var name: String
var active: Bool
var paused: Bool
var duration: Int
var remainingWhenPaused: Int?
var timerEndTime: Date?
var timerStartTime: Date?
var audioAlert: AlertNoise
var UUID: String
var colorScheme: BaseColor
var alarmRepetitions: Int
var timerRepetitions: Int
var currentTimerRepetition: Int
var audioPlaying: Bool
var player: AVAudioPlayer = AVAudioPlayer()
var countDownTimer: Timer = Timer()
var delegate: timerProtocol? = nil
init(withName name: String, duration: Int, UUID: String, color: BaseColor, alertNoise: AlertNoise, timerRepetitions: Int, alarmRepetitions: Int) {
self.name = name
self.active = false
self.paused = false
self.duration = duration
self.UUID = UUID
self.audioAlert = alertNoise
self.colorScheme = color
self.alarmRepetitions = alarmRepetitions
self.audioPlaying = false
self.timerRepetitions = timerRepetitions
self.currentTimerRepetition = 0
super.init()
}
convenience override init() {
self.init(withName: "Tap Timer 1", duration: 10, UUID: Foundation.UUID().uuidString, color: .Red, alertNoise: .ChurchBell, timerRepetitions: 1, alarmRepetitions: 0)
}
// MARK: NSCoding
required convenience init? (coder decoder: NSCoder) {
print("in init coder:")
print("Name: \(decoder.decodeObject(forKey: "name"))")
print("Duration: \(decoder.decodeObject(forKey: "duration"))")
guard let name = decoder.decodeObject(forKey: "name") as? String
else {
print("init coder name guard failed")
return nil
}
guard let duration = decoder.decodeObject(forKey: "duration") as? Int
else {
print("init coder duration guard failed")
print("duration: \(decoder.decodeObject(forKey: "duration"))")
return nil
}
guard let audioAlertRawValue = decoder.decodeObject(forKey: "audioAlert") as? String
else {
print("init coder audioAlert guard failed")
return nil
}
guard let UUID = decoder.decodeObject(forKey: "UUID") as? String
else {
print("init coder UUID guard failed")
return nil
}
guard let colorSchemeRawValue = decoder.decodeObject(forKey: "colorScheme") as? String
else {
print("init coder colorScheme guard failed")
return nil
}
guard let alarmRepetitions = decoder.decodeObject(forKey: "alarmRepetitions") as? Int
else {
print("init coder alarmRepetitions guard failed")
return nil
}
guard let timerRepetitions = decoder.decodeObject(forKey: "timerRepetitions") as? Int
else {
print("init coder timerRepetitions guard failed")
return nil
}
guard let audioAlert = AlertNoise(rawValue: audioAlertRawValue)
else{
print("No AlertNoise rawValue case found")
return nil
}
guard let colorScheme = BaseColor(rawValue: colorSchemeRawValue)
else{
print("No BaseColor rawValue case found")
return nil
}
print("initCoder guards passed, initing timer")
print("\(name), \(duration), \(UUID), \(colorScheme), \(audioAlert), \(timerRepetitions), \(alarmRepetitions)")
self.init(withName: name, duration: duration, UUID: UUID, color: colorScheme, alertNoise: audioAlert, timerRepetitions: timerRepetitions, alarmRepetitions: alarmRepetitions)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.duration, forKey: "duration")
aCoder.encode(self.audioAlert.rawValue, forKey: "audioAlert")
aCoder.encode(self.UUID, forKey: "UUID")
aCoder.encode(self.colorScheme.rawValue, forKey: "colorScheme")
aCoder.encode(self.alarmRepetitions, forKey: "alarmRepetitions")
aCoder.encode(self.timerRepetitions, forKey: "timerRepetitions")
}
So it seems the solution is simple if a little unintuitive.
So I encoded the class ivars with the general method encode(self.ivar, forKey: "keyName") however if that ivar is an int it needs to be decoded with decodeInteger(forKey: "keyName") - this entails getting rid of the guard statements as well since this method return an non-optional. Seems odd to have to decode with the integer specific method if it was decoded with the generalist method - this wasn't the case in Swift 2.2.
Excellent answer by SimonBarker and it solved the same issue I had. I eventually applied his solution to my own code and revised it too so that encoding is done with the generalist method. You can "force" encoding with the generalist method by using:
func encode(_ objv: Any?, forKey key: String)
So in your code you could use:
aCoder.encode(self.name as Any?, forKey: "name")
That way self.name is encoded as an object and does not break your existing code, namely: decoder.decodeObject(forKey: "name") as? String
This might not be the most elegant solution, but at least it worked for me without the need to change code that worked beautifully in Swift 2.3, but which was broken in Swift 3...
I have the following struct definition:
struct ThreadManager: Equatable {
let fid: Int
let date: NSDate
let forumName: String
let typeid: Int
var page: Int
var threadList: [Thread]
var totalPageNumber: Int?
}
and the thread is :
struct Thread: Equatable {
let author: Author
let replyCount: Int
let readCount: Int
let title: String
let tid: Int
let isTopThread: Bool
var attributedStringDictionary: [String: NSAttributedString]
var postDescripiontTimeString: String
var hasRead: Bool
}
How can I encode a ThreadManager variable to NSData? I tried to used the following functions, but it does not worK.
func encode<T>(var value: T) -> NSData {
return withUnsafePointer(&value) { p in
NSData(bytes: p, length: sizeofValue(value))
}
}
func decode<T>(data: NSData) -> T {
let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T))
data.getBytes(pointer, length: sizeof(T))
return pointer.move()
}
I have ThreadManager items, and I want to store them into sqlite. So I need to convert them to NSData. I have a variable called threadManager, the number of items in its threadList is about 70. I run the code and set a breakpoint, and input encode(threadManager) in xcode console, it is only 73bytes. It is wrong. How can I encode and decode those struct to NSData.
If your database is to be read on any other platform (Android, the web, wherever), you'd better choosing a cross-platform format such as JSON, or spread your struct members in their dedicated columns in a database table.
If you only target iOS/OSX/tvOS/etc, I recommend NSCoder. It is efficient, and most importantly:
NSCoder is platform-independant, which means that your NSData coding and decoding is not dependent on the particular memory layout currently used by the platform. For example, you don't have to fear 32 / 64 bits compatibility.
NSCoder lets you change your type over time, while keeping the ability to import old versions of your struct.
The code below adds a asData() function to your struct, and an init(data:) initializer. Those two let you go back and forth from your struct to NSData.
import Foundation
struct MyStruct {
let name: String
let date: NSDate
}
extension MyStruct {
init(data: NSData) {
let coding = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! Coding
name = coding.name as String
date = coding.date
}
func asData() -> NSData {
return NSKeyedArchiver.archivedDataWithRootObject(Coding(self))
}
class Coding: NSObject, NSCoding {
let name: NSString
let date: NSDate
init(_ myStruct: MyStruct) {
name = myStruct.name
date = myStruct.date
}
required init?(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObjectForKey("name") as! NSString
self.date = aDecoder.decodeObjectForKey("date") as! NSDate
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: "name")
aCoder.encodeObject(date, forKey: "date")
}
}
}
let encodedS = MyStruct(name: "foo", date: NSDate())
let data = encodedS.asData()
let decodedS = MyStruct(data: data)
print(decodedS.name)
print(decodedS.date)
#Gwendal Roué : you are right, but I have to build another class according to each struct. I used the following method, it is ugly, but it works. Can you help me to improve it?
init(data: NSData) {
let dictionary = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! NSDictionary
fid = (dictionary["fid"] as! NSNumber).integerValue
date = dictionary["date"] as! NSDate
forumName = dictionary["forumName"] as! String
typeid = (dictionary["typeid"] as! NSNumber).integerValue
page = (dictionary["page"] as! NSNumber).integerValue
totalPageNumber = (dictionary["totalPageNumber"] as? NSNumber)?.integerValue
let threadDataList = dictionary["threadDataList"] as! [NSData]
threadList = threadDataList.map { Thread(data: $0) }
}
extension ThreadManager {
func encode() -> NSData {
let dictionary = NSMutableDictionary()
dictionary.setObject(NSNumber(integer: fid), forKey: "fid")
dictionary.setObject(date, forKey: "date")
dictionary.setObject(forumName, forKey: "forumName")
dictionary.setObject(NSNumber(integer: typeid), forKey: "typeid")
dictionary.setObject(NSNumber(integer: page), forKey: "page")
if totalPageNumber != nil {
dictionary.setObject(NSNumber(integer: totalPageNumber!), forKey: "totalPageNumber")
}
let threadDataList: [NSData] = threadList.map { $0.encode() }
dictionary.setObject(threadDataList, forKey: "threadDataList")
return NSKeyedArchiver.archivedDataWithRootObject(dictionary)
}
}