Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save XML data to UserDefaults #22

Closed
meshileya opened this issue Apr 21, 2019 · 11 comments
Closed

Save XML data to UserDefaults #22

meshileya opened this issue Apr 21, 2019 · 11 comments
Labels

Comments

@meshileya
Copy link

Hello @gcharita , thanks for this great library. It is well appreciated.

I will like to ask how best I can store data to my UserDefaults.

I have already tried

func saveListData(podList: [RSSFeeds]){
        let podData = NSKeyedArchiver.archivedData(withRootObject: podList)
        UserDefaults.standard.set(podData, forKey: "podData")
    }

but i am getting the unrecognized selector sent to instance 0x600000c23040 error.

Any insight to getting this resolved will be well appreciated.

Regards,

@gcharita
Copy link
Owner

@meshileya thank you for choosing this library. 😃

To use NSKeyedArchiver your RSSFeeds type must implement NSCoding protocol (you can find an example on how to do that in this link)

If you don't want to use NSCoding protocol (given the fact that your RSSFeeds type implements XMLMappable protocol) you can try XMLSerialization to convert your XML into data like:

func saveListXMLData(podList: [RSSFeeds]) {
    let podData = try? XMLSerialization.data(withXMLObject: podList.toXML())
    UserDefaults.standard.set(podData, forKey: "podData")
    UserDefaults.standard.synchronize()
}

Just remember to convert the data back to your type, when you are retrieving them like:

func getListXMLData() -> [RSSFeeds]? {
    guard let data = UserDefaults.standard.data(forKey: "podData") else {
        return nil
    }
    let xml = try? XMLSerialization.xmlObject(with: data)
    return XMLMapper<RSSFeeds>().mapArray(XMLObject: xml)
}

On the other hand, you can just save your XML string to the UserDefaults like:

func saveListXMLString(podList: [RSSFeeds]) {
    UserDefaults.standard.set(podList.toXMLString(), forKey: "podData")
    UserDefaults.standard.synchronize()
}

And map it back to your object type, when you are retrieving it like:

func getListXMLString() -> [RSSFeeds]? {
    guard let xmlString = UserDefaults.standard.string(forKey: "podData") else {
        return nil
    }
    return XMLMapper<RSSFeeds>().mapArray(XMLString: xmlString)
}

All of the above ways will have the same outcome.

Hope this helps.

@meshileya
Copy link
Author

meshileya commented Apr 21, 2019

Thanks so much @gcharita , I really do appreciate this detailed response.

Just for clarity sake, I will like to know if there is a special way I can get the text of this xml.

<itunes:category text="Life & Love"/>

Thanks once again, I really do appreciate.
@gcharita

@gcharita
Copy link
Owner

@meshileya the & character is not allowed as is in an XML. This should be &amp; in order to parsed correctly by the system. (you can find more info on XML allowed characters in this link)

Having said that, if your XML looks like this:

<root>
    <itunes:category text="Life &amp; Love"/>
</root>

You can use nested attribute mapping to map text attribute with the following model:

class Root: XMLMappable {
    var nodeName: String!
    
    var itunesCategoryText: String?
    
    required init?(map: XMLMap) {}
    
    func mapping(map: XMLMap) {
        itunesCategoryText <- map.attributes["itunes:category.text"]
    }
}

Hope this helps.

@meshileya
Copy link
Author

Oh yeah, thanks @gcharita I really appreciate.

But I think I have got into a slight issue now.

Whenever I try to save the data using UserDefaults, it actually saves well but if I want to retrieve the data, the below is the error I do get.

The only weird part is, it actually works without having to save in UserDefaults but once that is done, then I get the error below.

I really appreciate your effort bro :(

Error Domain=NSXMLParserErrorDomain Code=68 "(null)" UserInfo={NSXMLParserErrorColumn=56, NSXMLParserErrorLineNumber=1, NSXMLParserErrorMessage=xmlParseEntityRef: no name
}

@gcharita
Copy link
Owner

@meshileya what method do you use to save to the UserDefaults? If you convert your XML to Data, try using the raw string method.

@meshileya
Copy link
Author

meshileya commented Apr 22, 2019

@gcharita

This is actually the way I am doing the write and read

 func savePod(podList: [RSSFeeds]) {
        UserDefaults.standard.set(podList.toXMLString(), forKey: "podData")
        UserDefaults.standard.synchronize()
    }
    
    func loadPod() -> [RSSFeeds]? {
        guard let xmlString = UserDefaults.standard.string(forKey: "podData") else {
            return nil
        }
        return XMLMapper<RSSFeed>().mapArray(XMLString: xmlString)
    }

It only happens when I have itunesCategoryText <- map.attributes["itunes:category.text"]

@gcharita
Copy link
Owner

@meshileya there is an interesting bug that I just figured it out. Sorry for the inconvenience. Take a look at #23 for a temporary fix.

@gcharita
Copy link
Owner

gcharita commented May 1, 2019

I am closing this, since the question was answered and the #23 is not related to this. @meshileya thank you for asking.

For questions regarding XMLMapper feel free to use Stack Overflow.

@gcharita gcharita closed this as completed May 1, 2019
@meshileya
Copy link
Author

meshileya commented May 15, 2019

Hello @gcharita , I will so much appreciate your insight as regards this.

I am currently working on an XML with several categories. e.g.

<itunes:category text="Society &amp; Culture"/>
        <itunes:category text="Comedy"/>
        <itunes:category text="Arts"/>

but, whenever I use category <- map.attributes["itunes:category.text"] , it returns null.

but it works perfectly well for XML with just one itunes:category.

I will like to ask if there is anything I am doing wrong, pls.

@gcharita
Copy link
Owner

gcharita commented May 15, 2019

@meshileya unfortunately, you have to create a new XMLMappable class-struct in order to map that XML. So you can try something like this:

class Root: XMLMappable {
    var nodeName: String!

    var itunesCategories: [ITunesCategory]?
    
    required init?(map: XMLMap) {}
    
    func mapping(map: XMLMap) {
        itunesCategories <- map["itunes:category"]
    }
}

class ITunesCategory: XMLMappable {
    var nodeName: String!
    
    var text: String?
    
    required init?(map: XMLMap) {}
    
    func mapping(map: XMLMap) {
        text <- map.attributes["text"]
    }
}

Don't worry if you receive only one itunes:category tag. XMLMapper will handle that and generate an Array with only one element 😉

@meshileya
Copy link
Author

You are really one of the genii of our time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants