How to set a readwrite internal, readonly external property in protocol - ios

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] { /* ... */ }
}

Related

Swift conform to protocol subclass

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
}

What am I doing wrong on passing data through protocol

I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!

Override swift protocol property to optional

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 }
}

Swift. Internal type in public protocol

I am trying to work with swift generics, but I am stuck...
It might be impossible to do it this way, but I was hoping someone would have a good suggestion.
So I have this protocol and a type that I want to have internal:
internal protocol ATCoreInstrumentProtocol { // SOME STUFF }
internal typealias AT_1G_WaveInstrumentType = //Somethings that conforms to ATCoreInstrumentProtocol
Then I have this InstrumentType that I want to be public.
The problem here is that ASSOCIATED_INSTRUMENT aka ATCoreInstrumentProtocol
needs to be internal and therefore I cannot use it in this way.
There is not an option to make the ATCoreInstrumentProtocol public.
public protocol InstrumentType {
static var instrumentType: SupportedInstrumentTypes { get }
associatedtype ASSOCIATED_INSTRUMENT: ATCoreInstrumentProtocol
}
public final class InstrumentTypes {
private init() {}
public final class AT_1G_Wave : InstrumentType {
public class var instrumentType: SupportedInstrumentTypes { get { return .wave_1G } }
public typealias ASSOCIATED_INSTRUMENT = AT_1G_WaveInstrumentType
}
}
This is how I want to use it.
internal class ATCoreInteractor<IT: InstrumentType> {
internal var instrumentObservable : Observable<IT.ASSOCIATED_INSTRUMENT> {
return self.instrumentSubject.asObservable()
}
private var instrumentSubject = ReplaySubject<IT.ASSOCIATED_INSTRUMENT>.create(bufferSize: 1)
internal init(withSerial serial: String){
self.scanDisposable = manager
.scanFor([IT.instrumentType])
.get(withSerial: serial)
.take(1)
.do(onNext: { (inst) in
self.instrumentSubject.onNext(inst)
})
.subscribe()
}
And then I have the ATSyncInteractor which is public
public final class ATSyncInteractor<IT : InstrumentType> : ATCoreInteractor<IT> {
public override init(withSerial serial: String) {
super.init(withSerial: serial)
}
}
public extension ATSyncInteractor where IT : InstrumentTypes.AT_1G_Wave {
public func sync(opPriority: BLEOperationPriority = .normal, from: Date, to: Date, callback: #escaping (ATCoreReadyData<AT_1G_Wave_CurrentValues>?, [WaveTimeSeriesCoordinator], Error?) -> Void) {
// DO SOMETHING
}
}
let interactor = ATSyncInteractor<InstrumentTypes.AT_1G_Wave>(withSerial: "12345")
This ended up beeing my solution..
The InstrumentType only contains the SupportedInstrumentTypes.
The ATCoreInteractor Is no longer generic, but the observer is of type ATCoreInstrumentProtocol.
The ATSyncInteractor extensions casts the instrument to its associated type.
I am open to improvements and suggestions.
public protocol InstrumentType : class {
static var instrumentType: SupportedInstrumentTypes { get }
}
public final class InstrumentTypes {
private init() {}
public final class AT_1G_Wave : InstrumentType {
private init() {}
public class var instrumentType: SupportedInstrumentTypes { get { return .wave_1G } }
}
}
public class ATCoreInteractor {
internal var instrumentSubject = ReplaySubject<ATCoreInstrumentProtocol>.create(bufferSize: 1)
internal init(for type: SupportedInstrumentTypes, withSerial serial: String){
self.scanDisposable = manager
.scanFor([type])
.get(withSerial: serial)
.take(1)
.do(onNext: { (inst) in
self.instrumentSubject.onNext(inst)
})
.subscribe()
}
}
public final class ATSyncInteractor<IT : InstrumentType> : ATCoreInteractor {
public init(withSerial serial: String) {
super.init(for: IT.instrumentType, withSerial: serial)
}
}
public extension ATSyncInteractor where IT : InstrumentTypes.AT_1G_Wave {
private func instrument() -> Observable<AT_1G_WaveInstrumentType> {
return self.instrumentSubject.map { $0 as! AT_1G_WaveInstrumentType }
}
public func sync(opPriority: BLEOperationPriority = .normal, from: Date, to: Date, callback: #escaping (ATCoreReadyData<AT_1G_Wave_CurrentValues>?, [WaveTimeSeriesCoordinator], Error?) -> Void) {
}
}

Instantiating classes stored in metatype Dictionary

I've followed the solution at Make a Swift dictionary where the key is "Type"? to create dictionaries that can use a class type as keys.
What I want to do is: I have one dictionary that should store class types with their class type (aka metatype) as keys, too:
class MyScenario {
static var metatype:Metatype<MyScenario> {
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
Then I have methods to register and execute scenarios:
public func registerScenario(scenarioID:MyScenario.Type) {
if (scenarioClasses[scenarioID.metatype] == nil) {
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type) {
if let scenarioClass = scenarioClasses[scenarioID.metatype] {
let scenario = scenarioClass()
}
}
... Problem is in the last line:
Constructing an object of class type 'MyScenario' with a metatype
value must use a 'required' initializer.
It looks like the compiler is confused at that point since I cannot use 'required' at that assignment. Does anyone have an idea how I would have to instantiate the scenarioClass in executeScenario()?
This must do the job.
import Foundation
struct Metatype<T> : Hashable
{
static func ==(lhs: Metatype, rhs: Metatype) -> Bool
{
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type)
{
self.base = base
}
var hashValue: Int
{
return ObjectIdentifier(base).hashValue
}
}
public class MyScenario
{
var p: String
public required init()
{
self.p = "any"
}
static var metatype:Metatype<MyScenario>
{
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
public func registerScenario(scenarioID:MyScenario.Type)
{
if (scenarioClasses[scenarioID.metatype] == nil)
{
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type)
{
if let scenarioClass = scenarioClasses[scenarioID.metatype]
{
let scenario = scenarioClass.init()
print("\(scenario.p)")
}
}
// Register a new scenario
registerScenario(scenarioID: MyScenario.self)
// Execute
executeScenario(scenarioID: MyScenario.self)
// Should print "any"

Resources