I am using this library, https://github.com/gcharita/XMLMapper.
It contains protocol,
public protocol XMLBaseMappable {
var nodeName: String! { get set }
}
I would like to make this nodeName optional for my struct/classes implementing it, like
public protocol CustomXMLBaseMappable {
var nodeName: String? { return "default" }
//I don't want struct/classes using it, implements `nodeName`.
}
Any suggestions will be helpful.
The existence of this property in XMLBaseMappable protocol is crucial in order to function correctly the whole library.
Having said that, you can't omit the implementation of this property in your structs and classes but you can "hide" it in a super class. Using this:
class BasicXMLMappable: XMLMappable {
var nodeName: String!
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
}
}
You can have XMLMappable objects that extends BasicXMLMappable and they don't have to implement nodeName property:
class TestBasicXMLMappable: BasicXMLMappable {
// Your custom properties
required init(map: XMLMap) {
super.init(map: map)
}
override func mapping(map: XMLMap) {
// Map your custom properties
}
}
Edit:
As of version 1.5.1 you can use XMLStaticMappable for implementing XMLMapper in an extension. For example:
class CustomClass {
var property: String?
}
extension CustomClass: XMLStaticMappable {
var nodeName: String! {
get {
return "default"
}
set(newValue) {
}
}
static func objectForMapping(map: XMLMap) -> XMLBaseMappable? {
// Initialize CustomClass somehow
return CustomClass()
}
func mapping(map: XMLMap) {
property <- map["property"]
}
}
Hope this helps.
You can't override the original protocol, but you could manually change it like that:
#objc protocol XMLBaseMappable {
#objc optional var nodeName: String! { get set }
}
or leave it as it is, and create your custom protocol
#objc protocol CustomXMLBaseMappable {
#objc optional var nodeName: String! { get set }
}
Related
Within my app, I have multiple UIView subclasses that depend on a model. Each of the classes adopting 'Restorable' protocol which holds the superclass of the model. Each sub-model describes the specific UIView not-common properties.
// Super-model
public protocol StoryItem {
var id: Int64? { get }
}
// Parent protocol
public protocol Restorable: AnyObject {
var storyItem: StoryItem? { get set }
}
// Specific protocol
public struct TextItem: StoryItem {
public var id: Int64?
public var text: String?
}
// Not complling
class ResizableLabel: UILabel, Restorable {
var storyItem: TextItem?
}
I'm getting the following compiler error:
*Type 'ResizableLabel' does not conform to protocol 'Restorable'*
The only way I can make it compile is by changing ResizableLabel to
// Works
class ResizableLabel: UILabel, Restorable {
var storyItem: StoryItem?
}
Is there any way to conform to protocol subclass? it'll make the Init process much cleaner. Thank you for your help!
Change
public protocol Restorable: AnyObject {
var storyItem: StoryItem? { get set } // adopter must declare as StoryItem
}
to
public protocol Restorable: AnyObject {
associatedtype T : StoryItem
var storyItem: T? { get set } // adopter must declare as StoryItem adopter
}
Now your code compiles. Full example:
public protocol StoryItem {
var id: Int64? { get }
}
public protocol Restorable: AnyObject {
associatedtype T : StoryItem
var storyItem: T? { get set }
}
public struct TextItem: StoryItem {
public var id: Int64?
public var text: String?
}
class ResizableLabel: UILabel, Restorable {
var storyItem: TextItem? // ok because TextItem is a StoryItem adopter
}
I have some classes have same behaviors, they all have
properties > savedPath: String, items: [String], currentItem: [String]
functions > archive(with items: [String]), unarchive()
So I create a protocol.swift and let those classes conform this protocol to implement these common behavior.But, in my case, i want:
items is a readonly external, readwrite internal
archive/unarchive are private func
i tried to use private(set) before items, private before archive/unarchive and some errors showed up.
Is there any flexible solutions to fix that?
before without protocol
class SampleClass {
private(set) var items: [SampleModel] {
set {
archive(with: newValue)
} get {
return unarchive()
}
}
func archive(with addresses: [SampleModel]) { ... }
func unarchive() -> [SampleModel] { ... }
}
after try to use protocol to satisfy
protocol SampleProtocol {
associatedtype Item: Switchable
var savedPath: String { get }
var items: [Item] { get }
var currentItem: Item? { get }
func archive(with items: [Item])
func unarchive() -> [Item]
}
You must remove any private function from protocol (Since it's meaningless). Nothing is private inside a protocol( setter or function or etc. )
protocol SampleProtocol {
associatedtype Item: Switchable
var savedPath: String { get }
var items: [Item] { get }
var currentItem: Item? { get }
}
And you should then implement the class access controls like this:
class SampleClass {
private(set) var items: [SampleModel] {
set {
archive(with: newValue)
} get {
return unarchive()
}
}
private func archive(with addresses: [SampleModel]) { /* ... */ }
private func unarchive() -> [SampleModel] { /* ... */ }
}
I am using ObjectMapper. And I know we can specify the keypath like map["name.label"] but I don't want to use keyPath at a moment. Check the below code. I can access name like Author.name?.label.
class Author: Mappable {
var name: LabelDict?
required init?(map: Map) {
}
func mapping(map: Map) {
name <- map["name"]
}
}
class LabelDict: Mappable {
var label: String?
required init?(map: Map) {
}
func mapping(map: Map) {
label <- map["label"]
}
}
How can I set the getter and setter methods of the name property of Author class to set the value as LabelDict class label and when I get the value I get the String directly as Author.name. I can do it by using one different variable but is it possible to do with the same?
You could make your LabelDict adopt CustomStringConvertible protocol.
class LabelDict: Mappable, CustomStringConvertible {
var label: String?
var description: String {
get {
return self.label ?? ""
}
}
required init?(map: Map) {
}
func mapping(map: Map) {
label <- map["label"]
}
}
Then you'd use it like this String(describing: myLabelDictInstance).
-- Clarification
To simply print the label into console you can now use print(Author?.name). If you want to assign it to label for example, you can use someLabel.text = String(describing: Author?.name)
using the following simplified structure:
class Property: Mappable {
var path: String?
override func mapping(map: Map) {
path <- map["path"]
}
}
class Specification {
enum Name: String {
case Small = "SMALL"
case Medium = "MEDIUM"
}
}
class ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
override func mapping(map: Map) {
properties <- (map["properties"], EnumTransform<Specification.Name>())
}
}
... with that JSON:
[{"properties: ["SMALL": {"path": "http://..."}, "MEDIUM": {"path": "http://..."}]}]
... produces when using EnumTransform() as Transform the following (reasonable) compile error:
Binary operator '<-' cannot be applied to operands of type '[Specification.Name : Property]?' and '(Map, EnumTransform<Specification.Name>)'
So how does a custom TransformType have to look like, to map that dictionary the right way?
You can find the source of EnumTransform here: https://github.com/Hearst-DD/ObjectMapper/blob/master/ObjectMapper/Transforms/EnumTransform.swift
Thanks!
TransformTypes are IMHO primary designed to transform values and not keys. And your example is a little bit complicated because even value is not just basic type.
What do you think about this little hack?
struct ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
init?(_ map: Map) {
}
mutating func mapping(map: Map) {
let stringProperties: [String: Property]?
// map local variable
stringProperties <- map["properties"]
// post process local variable
if let stringProperties = stringProperties {
properties = [:]
for (key, value) in stringProperties {
if let name = Specification.Name(rawValue: key) {
properties?[name] = value
}
}
}
}
}
We should use DictionaryTransform instead of EnumTransform. We are transforming type of Dictionary [String:Any] to [Key:Value]. In our case type is [Specification.Name: Property].
Key need to conforms protocols like Hashable, RawRepresentable, and Key.RawValue should be String.
And Value Should be conforms Mappable Protocol.
class Property: Mappable {
var path: String?
override func mapping(map: Map) {
path <- map["path"]
}
}
class Specification {
enum Name: String {
case Small = "SMALL"
case Medium = "MEDIUM"
}
}
class ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
override func mapping(map: Map) {
properties <- (map["properties"], DictionaryTransform<Specification.Name,Property>())
}
}
It seems a class, which uses generics in swift, sometimes cannot properly determine object type.
Consider the following model structure:
class BaseModel: NSObject, Equatable, Printable {
var id: String = ""
override var description: String {
return "id: \(id)"
}
override func isEqual(object: AnyObject?) -> Bool {
if let object = object as? BaseModel {
return object.id == id
}
else {
return super.isEqual(object)
}
}
}
class Image: BaseModel {
var image: UIImage!
}
I also have parsers, which should parse/serialize objects:
class AbstractParser<T: BaseModel where T: Equatable>: NSObject {
func convertFromParseObject(object: NSObject) -> T {
var entity = T()
......
return updateEntityWithParseObject(object, entity: entity)
}
func updateEntityWithParseObject(object: NSObject, entity: T) -> T {
fatalError("This method must be overridden")
}
}
class ImageParser<T: Image>: AbstractParser<Image> {
override func updateEntityWithParseObject(object: NSObject, entity: Image) -> Image {
println("\(entity)")
println("\(entity.id)")
// The line below outputs BaseModel, shouldn't it be Image instead?
println("\(NSStringFromClass(entity.classForCoder))")
// EXC_BAD_ACCESS here:
println("\(entity.image)")
return entity
}
}
The app crashes when I try to access entity.image.
For some reasons Swift thinks that entity object is BaseModel, not Image.
Playground file: https://drive.google.com/file/d/0B6agzpK_lR6JQUlhMFoxaGw1akU/view?usp=sharing