As being new to Swift
i don't know how to serialize a class into xml
Class Employee
{
var mName : String = ""
var Name : String
{
get
{
return mName
}
set
{
mName = newValue
}
}
var mDesingation : String = ""
var Desingation: String
{
get
{
return mDesingation
}
set
{
mDesingation = newValue
}
}
}
I have searched a lot but haven't come across any XML Serialization Engine for Swift.
XML Serialization
For XML serialization, I suggest you use following library:
https://github.com/skjolber/xswi
As the usage is quite easy but well documented, I won't copy it here, instead you can just use examples that they provide. Since your class is very easy, it is sufficient solution. AFAIK there is no library that provides automatic serialization, because it is not used on iOS. Core data provide you with option to serialize as XML, but that is highly problematic and mostly not usable for what you want.
NSCoding / NSKeyedArchiver
If you need to just store the class on the disk and load it again, there is better option, and that is to use NSKeyedArchiver / NSCoding protocol. Again, there is great article about how to use it with extensive examples, so just the basics:
You extend your class so it conforms to NSCoding protocol
You write implementation of two methods - encodeWithCoder: and initWithDecoder:
You use NSKeyedArchiver to archive your class
You write NSData that you save to disk (and vice versa)
Hope it helps!
Related
I have a pretty complicated structure with Generic types in my app. This works, but there is an issue, that at the end of this chain, I need to specify some types 2 times, because they need to be used as generics of some class, and one of these generic types, also need generic type. Which are always the same types as these before it. It goes like this <A, B, C<A, B>>
This makes it a little unpleasant to use. Is there some way to make it infer A and B from C
Here is sample code, with stripped functionalities:
// MARK: - Base classes that Im using, stripped from funcionalities.
// This one is a base for performing some detection. It can return any type as a result of scanning.
class DetectionPerformer<ResultType> {}
// This one adds possibility to load some model needed to perform scanning from the disk.
class LocalFileDetectionPerformer<ResultType, LocalModelType>: DetectionPerformer<ResultType> {
required init(localModelURL: URL) {}
}
// This one adds possibility to download this model and store it on the disk before loading.
class DownloadableDetectionPerformer<ResultType, LocalModelType>: LocalFileDetectionPerformer<ResultType, LocalModelType> {}
// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<ResultType, LocalModelType, LocalFileDetectionPerformerType: DownloadableDetectionPerformer<ResultType, LocalModelType>>: DetectionPerformer<ResultType> {
private let localFileDetectionPerformer: LocalFileDetectionPerformerType
init(remoteModelURL: URL) {
let localModelURL = Self.localModelURL(for: remoteModelURL)
localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
}
static func localModelURL(for url: URL) -> URL {
url.appendingPathExtension("local")
}
}
// Detector is main object in application. It takes some type of Detector as init parameter, and works on it.
class Detector<ResultType, DetectionPerformerType: DetectionPerformer<ResultType>> {
let performer: DetectionPerformerType
init(performer: DetectionPerformerType) {
self.performer = performer
}
}
// Now I can implement some specific performers, whcich will do real work. For example:
class SamplePerformer: DownloadableDetectionPerformer<Int, String> {}
// And I'm able to create Detector with any type of Performer:
let detectorA = Detector(performer: SamplePerformer(localModelURL: URL(string: "")!))
// The problem begins, when I want to wrap Performer into RemoteFileDetectionPerformer
let detectorB = Detector(performer: RemoteFileDetectionPerformer<Int, String, SamplePerformer>(remoteModelURL: URL(string: "")!))
// Here I need to specify all 3 generic types of RemoteFileDetectionPerformer, even tough two first are always the same as generic types of SamplePerformer. I can't even specify different ones, as this would create an error.
// Is there some way for RemoteFileDetectionPerformer to infer these first two generic types from LocalFileDetectionPerformerType? Maybe I need to construct these some differently?
I feel like the classes you showed in the first half of the code block should be protocols instead. That is, DetectionPerformer, LocalFileDetectionPerformer, DownloadableDetectionPerformer should all be protocols. They don't seem to have any real implementation in them, as is evident in your comment "Now I can implement some specific performers, which will do real work". If you have any implementations that you want to put in them, you can put it in an extension most of the time. Why making them protocols solves the problem? Because then we can use associated types rather than type parameters.
protocol DetectionPerformer {
associatedtype ResultType
}
// This one adds possibility to load some model needed to perform scanning from the disk.
protocol LocalFileDetectionPerformer: DetectionPerformer {
associatedtype LocalModelType
init(localModelURL: URL)
}
// This one adds possibility to download this model and store it on the disk before loading.
protocol DownloadableDetectionPerformer: LocalFileDetectionPerformer {}
// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<LocalFileDetectionPerformerType: DownloadableDetectionPerformer>: DetectionPerformer {
typealias ResultType = LocalFileDetectionPerformerType.ResultType
private let localFileDetectionPerformer: LocalFileDetectionPerformerType
init(remoteModelURL: URL) {
let localModelURL = Self.localModelURL(for: remoteModelURL)
localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
}
static func localModelURL(for url: URL) -> URL {
url.appendingPathExtension("local")
}
}
class Detector<DetectionPerformerType: DetectionPerformer> {
let performer: DetectionPerformerType
init(performer: DetectionPerformerType) {
self.performer = performer
}
}
class SamplePerformer: DownloadableDetectionPerformer {
required init(localModelURL: URL) {
}
typealias ResultType = Int
typealias LocalModelType = String
}
This allows you to do:
let detectorB = Detector(performer: RemoteFileDetectionPerformer<SamplePerformer>(remoteModelURL: URL(string: "")!))
First, I strongly agree with Sweeper. This is almost certainly a poor use of class inheritance and generics in Swift. Generally, if you think you want class inheritance, you should reach for other tools first. First, composition (could you just pass functions or bundles of functions). And then protocols. Looking at your types, it feels like you should be splitting up "thing that fetches data" from "thing that detects result in data" and composing the two.
That said, the problem is general and very legitimate, and there is a solution. Pass the type as a parameter:
init(performer: LocalFileDetectionPerformerType.Type, remoteModelURL: URL) { ... }
Then, when you call it, rather than explicitly type-specifying it, pass the type:
let detectorB =
Detector(performer: RemoteFileDetectionPerformer(performer: SamplePerformer.self,
remoteModelURL: URL(string: "https://example.com")!))
The type will automatically be worked out:
Detector<Int, RemoteFileDetectionPerformer<Int, String, SamplePerformer>>
If there are cases where the type would be known from context by other means, you can add a default parameter:
init(performer: LocalFileDetectionPerformerType.Type = LocalFileDetectionPerformerType.self, ...
Then you can leave off the parameter when it's not necessary.
Currently I have been working on a task of converting code from objective c to swift. The work was going smooth until I occured with a common resuable code that works in objective c but I haven't getting any idea how should I do that in swift.
The scenario working in objective c is.
I have a common function in my dataManager class
- (void)saveRequest:(id)request forId:(NSNumber *)requestId {
WebRequest *requestData = [[WebRequest alloc] initWithEntity:[NSEntityDescription entityForName:WEB_REQUEST inManagedObjectContext:self.context] insertIntoManagedObjectContext:self.context];
requestData.data = [request toJSON];
requestData.requestId = requestId;
requestData.timestamp = [NSDate date];
[self save];
}
in my project the request classes are already created which contains the toJSON function.
from my controller according to user changes I created the request object and passes the request object to this function and this function calls the toJSON function in the request class and everything works in objective c.
But when I convert this function in swift then it didn't support id as function input variable and if I use Any in place of id then it gives an error that Any don't have any toJSON function.
As this function is common different request objects will come from different controllers.
I don't have any idea how should I go further from hear, If anyone have any idea please help me out
Your class should be like
class WebRequest:NSObject
{
var data :Data?
var requestId: NSNumber?
var timestamp: Date?
init(entity:String , insertIntoManagedObjectContext:NSManagedObjectContext)
{
//your code here
}
}
and your code will be as follows
func saveRequest(request:Request, requestId:NSNumber)
{
let requestData = WebRequest(entity: "entityName", insertIntoManagedObjectContext:self.context)
requestData.data = request.toJSON();
requestData.requestId = requestId;
requestData.timestamp = Date()
}
and Request class in which toJson() present
class Request: NSObject
{
//has some members
func toJSON()->Data
{
return Data()
}
}
There is an existing Swift protocol, Codable (or you can do just Encodable if you want, as Codable is merely Encodable and Decodable), which is designed explicitly for representing an object in JSON (or other formats).
You then use JSONEncoder (rather than JSONSerialization, for example) to encode the object into JSON. See Encoding and Decoding Custom Types:
Consider a Landmark structure that stores the name and founding year of a landmark:
struct Landmark {
var name: String
var foundingYear: Int
}
Adding Codable to the inheritance list for Landmark triggers an automatic conformance that satisfies all of the protocol requirements from Encodable and Decodable:
struct Landmark: Codable {
var name: String
var foundingYear: Int
}
You can then do:
let landmark = Landmark(name: "Big Ben", foundingYear: 1859)
do {
let data = try JSONEncoder().encode(landmark)
print(String(data: data, encoding: .utf8)!)
} catch {
print(error)
}
That will product JSON like so:
{
"name": "Big Ben",
"foundingYear": 1859
}
See that Encoding and Decoding Custom Types for more information.
But, if you make your types Codable/Encodable, you could then retire your toJSON method entirely. There’s no need to write code to encode JSON anymore.
If you’re looking for a more tactical edit to your project as you convert it from Objective-C to Swift, you could define your own protocol, say JsonRepresentable, that has a single method requirement, your toJSON (or to whatever you’ve renamed this method during your conversion process).
protocol JsonRepresentable {
func toJSON() -> Data
}
And then, for all of the types that have implemented this method, just add this conformance.
Ideally, go back to those individual files and move the method into an extension for that protocol, e.g., for your first object type:
extension RequestObject1: JsonRepresentable {
func toJSON() -> Data {
...
}
}
And for your second:
extension RequestObject2: JsonRepresentable {
func toJSON() -> Data {
...
}
}
Etc.
is not there a simpler way rather than changing it in whole project
I would suggest that the above is best, but, if you don’t want to go back to all of those individual type declarations, you can just add conformance with an empty extension right where you defined JsonRepresentable:
extension RequestObject1: JsonRepresentable { }
extension RequestObject2: JsonRepresentable { }
As long as those types have implemented that method, these extensions will let the compiler know about their conformance to your protocol.
Anyway, this method can then use this protocol:
func save(_ request: JsonRepresentable, requestId: Int) {
let requestData = ...
requestData.data = request.toJSON()
requestData.requestId = requestId
requestData.timestamp = Date()
save()
}
Is it possible for a generic method to infer its type based on the class in which it is being executed? I use CoreData NSManagedObject models to store and retrieve local data, and have managed to make everything generic in an easy to read and usable way, except for in one place. If a user wishes to query the local database to fetch a list of objects, he would write the following line:
let posts: [Post] = Post.all()
This will properly return "all" Post objects in the database, but the syntax requires that the type be defined ([Post]) on top of calling the method from the Post class itself (Post.all()), which feels unnecessarily redundant. Is there any way to define the generic type simply by calling the all() method from the Post class? I imagine I could just create global functions for fetching data, like so:
let posts: [Post] = all()
This doesn't feel nearly as readable as it would be if the syntax was as follows:
let posts = Post.all()
The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future, regardless of if someone is working on it or just reading it for some other reason.
For more insight, here is a bit more information about the current structure:
//Model.swift - The model base class. All models extend this class.
class Model: NSManagedObject {
/**
Some other stuff here
**/
//MARK: Fetch
internal class func fetch<T: Model>(predicate: NSPredicate? = nil) -> [T]? {
do {
if let request = NSFetchRequest.FromEntityName(self.entityName) { //Get entity with the name defined in the current class
request.predicate = predicate
if let result = try self.context?.executeFetchRequest(request) as? [T] {
return result
}
}
}
catch let error as NSError {
Log.Error("\(error)")
}
return nil
}
//MARK: Fetch general
class func all<T: Model>() -> [T]? {
if let result: [T] = self.fetch() {
return result
}
Log.warning("No \(self.entityName) found")
return nil
}
}
//Post.swift - An example model class. Extends Model.swift
class Post: Model {
//some fields
}
//Example view controller
class ViewController: UIViewController {
override func viewDidLoad() {
let posts: [Post] = Post.all()
//do stuff
}
}
If anyone has an idea about then please let me know. All help is appreciated!
In the general case, the typical way for a class method to return "type of the class" even for subclasses is to use protocol extensions and the Self type. Here's an example that boils your approach down to the bare minimum to make the type checking work the way you want:
// define a protocol
protocol ModelType {}
// create a static method on the protocol that returns [Self]
extension ModelType where Self: NSManagedObject {
static func all() -> [Self]? {
return [Self]() // do your fetch here
}
}
// conform to the protocol in your class hierarchy
class Model: NSManagedObject, ModelType {}
class Post: Model {}
let posts = Post.all()
// implicit type of `posts` is `[Post]?`
Note that all() should be provided by the protocol extension, but not a requirement of the protocol. If you declare all() inside protocol ModelType, then you can't make it use dynamic dispatch, which is necessary if it's to use a dynamic type.
Also, note that in Swift 3 (and macOS 10.12 / iOS 10 / tvOS 10 / watchOS 3), Core Data itself defines some Swift API shortcuts that replace some of the ones you've defined for yourself. Note this example from What's New in Core Data:
func findAnimals() {
context.performAndWait({
let request = Animal.fetchRequest // implicitly NSFetchRequest<Animal>
do {
let searchResults = try request.execute()
// use searchResults ...
} catch {
print("Error with request: \(error)")
}
})
}
Finally, some commentary on your choice of style...
fyi I capitalize the first letter in all static/class methods just as a convention
The point of trying to improve this is so that any developers who pick up this project can quickly learn the structure and style without much effort. Also, this will hopefully increase general code readability in the future
I'm not sure that breaking from language-standard conventions (like the lowercase method names recommended in the Swift 3 API Guidelines) is very compatible with your goal of making it easy for other developers new to your codebase to read and participate.
I have a CoreDataStore class which has two generic placeholders and can be used for each entity type in the model. The idea is that it fetches an NSManagedObject subclass (based on one of the generic types) from the store, converts it into the appropriate object (based on the other generic type) and returns that object.
The purpose of this behaviour is so I'm keeping the Core Data aspects encapsulated and avoiding passing NSManagedObject instances all around the app.
Example potential usage
This is purely how the usage might look to further demonstrate what I am trying to achieve.
let personStore = CoreDataStore<ManagedPerson, Person>()
let personData = personStore.fetchSomeObject() // personData is a value type Person
I have the following code, separated over several files but shown here in a modified fashion for simplicity.
import Foundation
import CoreData
// MARK: - Core Data protocol and managed object
protocol ManagedObjectProtocol { }
class ManagedPerson: NSManagedObject, ManagedObjectProtocol {
var title: String?
}
class ManagedDepartment: NSManagedObject, ManagedObjectProtocol {
var name: String?
}
// MARK: - Simple struct representations
protocol DataProtocol {
typealias ManagedObjectType: ManagedObjectProtocol
init(managedObject: ManagedObjectType)
}
struct Person {
var title: String?
}
struct Department {
var name: String?
}
extension Person: DataProtocol {
typealias ManagedObjectType = ManagedPerson
init(managedObject: ManagedPerson) {
self.title = managedObject.title
}
}
extension Department: DataProtocol {
typealias ManagedObjectType = ManagedDepartment
init(managedObject: ManagedDepartment) {
self.name = managedObject.name
}
}
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol> {
func fetchSomeObject() -> DataObject {
var managedObject: ManagedObject // fetch an NSManagedObject
// Error here
return DataObject(managedObject: managedObject)
}
}
The error I am receiving is when I try to initialise the struct in fetchSomeObject:
Cannot invoke initializer for type 'DataObject' with an argument list of type '(managedObject: ManagedObject)'
Obviously the compiler can't figure out that the DataObject (which is restricted to types conforming to DataProtocol) can be initialised with a ManagedObject (which is restricted to types conforming to ManagedObjectProtocol) despite it being declared as such in DataProtocol.
Is there any way to achieve this functionality? Additionally is this a reasonable approach or am I completely off the wall with this?
Update
After a bit of digging it seems that Swift generics are invariant which I believe is causing what I'm running into.
Think your CoreDataStore again, for example, CoreDataStore<ManagedPerson, Department> doesn't make any sense. Why not? Because the Department is a DataProtocol without problem, but its corresponding typealias ManagedObjectType is not ManagedPerson.
The reason why your code won't compile is just the same. Here return DataObject(managedObject: managedObject) you can't initialize an DataObject from an armbitary ManagedObject, only a DataObject.ManagedObjectType is acceptable.
So what you need is a type constraint, add this where clause, your code should work:
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol
where DataObject.ManagedObjectType == ManagedObject>
I am currently working on an iOS app using Swift. I have a custom DataSource that needs to know the Type of the model that it has to provide. I have a custom Protocol, that has one method.
protocol JSONModel {
init(json: JSON)
}
Then, there are several models that implement the protocol. All of them have different properties, but are the same otherwise.
class CurrentDownload: JSONModel {
let someProperty: String
required init(json: JSON) {
someProperty = json["someProperty"].stringValue
}
}
My DataSource has a property that is of type JSONModel.Type
private let modelClass: JSONModel.Type
When i try to initialize a new instance of my modelClass i get a segmentation fault. Initialization of the model is done by
let model = modelClass(json: modelJSON)
Unfortunately, the compiler crashes on that line.
Swift Compiler Error
Command failed due to signal: Segmentation fault: 11
1. While emitting IR SIL function
#_TFC14pyLoad_for_iOS21RemoteTableDataSourceP33_56149C9EC30967B4CD75284CC9032FEA14handleResponsefS0_FPSs9AnyObject_T_ for 'handleResponse' at RemoteTableDataSource.swift:59:13
Does anybody have an idea on how to fix this or on how to work around this issue?
I believe this problem isn't too hard.
Usually with Swift segmentation faults are when you try to set the value of a constant (let) quantity, or try to set the value of something that hasn't been properly declared.
I can spot one such instance here:
required init(json: JSON) {
bytesLeft = json["someProperty"].stringValue
}
There's two things wrong with this example. You have a (designated) initialiser which terminates without setting the property someProperty and you haven't declared the variable bytesLeft.
So now the problem (which I really should have spotted before) is that * modelClass* just is not a class (or otherwise initialisable type). To cannot directly access a protocol, you can only access a class conforming to a protocol. The compiler didn't spot this because you did something sneaky with .Type.
When I say access, I mean functions and properties, including initialisers.
Only a class can create an instance of a class, which has itself as the type of the instance, and protocols can't have bonafide instances themselves.
If you think carefully about it, it is impossible to well-define what you are trying to do here. Suppose we had the protocol
protocol JSONModel {
init(json: JSON)
}
but then two classes:
class CurrentDownload: JSONModel {
let someProperty: String
required init(json: JSON) {
//Some other code perhaps.
someProperty = json["someProperty"].stringValue
}
}
class FutureDownload: JSONModel {
let differentProperty: String
required init(json: JSON) {
//Different code.
differentProperty = json["differentProperty"].stringValue
}
}
whereas before one might argue that
JSONModel.Type(json: JSON)
(this code is equivalent to your code) should compile and run because we have given an implementation of init, we now can't deny that there is confusion - which implementation should we use here?? It can't choose one, and most would argue that it shouldn't try, and so it doesn't.
What you need to do is initialise with some class.
If you are looking for a minimal class that adheres to JSONModel and no more, you'll need to write such a class e.g.
class BasicJSONModel: JSONModel {
let someProperty: String
required init(json: JSON) {
//Some other code perhaps.
someProperty = json["someProperty"].stringValue
}
}
Then do
private let modelClass: BasicJSONModel//.Type not needed with a class
let model = modelClass(json: modelJSON)
of course this is probably silly and one may just write
let model = BasicJSONModel(json: modelJSON)
Or you could just write a common superclass if the protocol's only use is for this:
class SuperJSONModel {
let someProperty: String//This is not needed of course and you could just strip down to the init only.
init(json: JSON) {
//Some other code perhaps.
someProperty = json["someProperty"].stringValue
}
}
and then is you wanted to check that some object is "conforming" to the what was the JSONModel, you simply check that it is a subclass of SuperJSONModel.