Singleton class with instance variable and methods in Swift (iOS) - ios

So, i am creating a Singleton class as below, and i need few instance variables in this class, such that any team member can access the instance variable and get the values. To do that, i will need to initialize these instance variables to a certain value at the beginning itself.
But i get a compilation error, saying "missing argument for parameter 'doesValueExists' in call".
What exactly i m doing wrong here ?
class ABC_Util {
private var doesValueExists: Bool
private var arrValues: NSMutableArray?
class var sharedInstance: ABC_Util {
struct ABC_UtilSingleton {
static let instance = ABC_Util()
}
return ABC_UtilSingleton.instance
}
init(doesValueExists: Bool, arrValues: NSMutableArray?) {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists() -> Bool {
}
//method
internal func getArrayOfValues() -> NSMutableArray? {
}
}

Your initializer for ABC_Util is declared as:
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
Therefore you cannot say
static let instance = ABC_Util()
The expression ABC_Util() would correspond to an initializer with no parameters, and you do not have such an initializer. You must say:
static let instance = ABC_Util(doesValueExists:someBool, arrValues:someArray)
(with appropriate values, of course).

You have to use your initializer in order to initialize your variables.
class ABC_Util {
private var doesValueExists:Bool
private var arrValues:NSMutableArray?
class var sharedInstance: ABC_Util
{
struct ABC_UtilSingleton
{
static let instance = ABC_Util(doesValueExists: true, arrValues: nil)
}
return ABC_UtilSingleton.instance
}
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
self.doesValueExists = doesValueExists
self.arrValues = arrValues
}
//method
internal func checkValueExists()-> Bool
{
return true
}
//method
internal func getArrayOfValues()-> NSMutableArray?
{
return nil
}
}
And I recommend you to change your singleton declaration to the suggested syntax
static let sharedInstance: ABC_Util = ABC_Util(doesValueExists: true, arrValues: nil)

You could use as below.
class ABC_Util {
private var doesValueExists:Bool = false
private var arrValues:NSMutableArray?
class var sharedInstance: ABC_Util {
struct ABC_UtilSingleton {
static let instance = ABC_Util(doesValueExists: false, arrValues: ["a", "b", "c"])
}
return ABC_UtilSingleton.instance
}
init(doesValueExists:Bool, arrValues:NSMutableArray?) {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists()-> Bool{
return self.doesValueExists
}
//method
internal func getArrayOfValues()-> NSMutableArray?{
return arrValues
}
}

i got the solution, when i tried this, worked fine!
class ABC_Util {
var doesValueExists:Bool = false
var arrValues:NSMutableArray? = nil
class var sharedInstance: ABC_Util
{
struct ABC_UtilSingleton
{
static let instance = ABC_Util()
}
return ABC_UtilSingleton.instance
}
init() {
self.doesValueExists = self.checkValueExists()
self.arrValues = self.getArrayOfValues()
}
//method
internal func checkValueExists()-> Bool
{
//return true/false
}
//method
internal func getArrayOfValues()-> NSMutableArray?
{
//return array/nil
}
}

Related

Access function protocol in generic class

I want to access function in protocol, but XCode complaint
Instance member 'createColumns' cannot be used on type 'T'; did you
mean to use a value of this type instead?
What I have done:
Create protocol:
protocol StorageModelDelegate {
func createColumns(for tableBuilder: TableBuilder)
}
Create class generic that receive StorageModelDelegate:
class SQLiteStorage<T: StorageModelDelegate> {
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder) // -> this is the error comes up.
}
}
}
Create class that implement SQLiteStorage:
final class InfoStorageModel {
private let sqlite: SQLiteStorage = SQLiteStorage<Info>()
}
so, how to fix the error in SQLiteStorage class?
The error indicates that you need an instance of T, not the type itself.
So you need something like:
class SQLiteStorage<T: StorageModelDelegate> {
var delegate:T
init (delegate:T) {
self.delegate = delegate
}
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
self.delegate.createColumns(for: builder) // -> this is the error comes up.
}
}
}
You want to call static method instead of instance method.
In order to fix, you should add instance parameter:
First of all, use weak var delegate in order to prevent retain cycles.
protocol StorageModelDelegate: class {
func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
delegate?.createColumns(for: builder)
}
}
}
Or use static protocol methods:
protocol StorageModelDelegate {
static func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder)
}
}
}

Setters and Getters with Generics Swift

I was wondering if there is a way to do this in a better way with generics. I have this singleton which needs a setter and another getter for every object. Plus, I have to check that the property is not nil in every getter, which is a lot of repeated code. ex:
class DataManager : NSObject {
private override init(){}
private var postData : [PostModel]?
private var userData : [UserModel]?
private var commentsData : [CommentsModel]?
private var photosData : [PhotosModel]?
private var albumsData : [AlbumsModel]?
private var todosData : [TodosModel]?
static let shared = DataManager()
//MARK : Setters
func setUserData(data : [UserModel]) {
self.userData = data
}
func setPostData(data : [PostModel]) {
self.postData = data
}
func setCommentsData(data : [CommentsModel]) {
self.commentsData = data
}
func setAlbumsData(data : [AlbumsModel]) {
self.albumsData = data
}
func setPhotosData(data : [PhotosModel]) {
self.photosData = data
}
func setTodosData(data : [TodosModel]) {
self.todosData = data
}
//MARK : Getters
func getUserData() -> [UserModel]? {
if self.userData != nil {
return self.userData!
}
return nil
}
func getPostData() -> [PostModel]? {
if self.postData != nil {
return self.postData!
}
return nil
}
func getCommentsData() -> [CommentsModel]? {
if self.commentsData != nil {
return self.commentsData!
}
return nil
}
func getAlbumsData() -> [AlbumsModel]? {
if self.albumsData != nil {
return self.albumsData!
}
return nil
}
func getPhotosData() -> [PhotosModel]? {
if self.photosData != nil {
return self.photosData!
}
return nil
}
func getTodosData() -> [TodosModel]? {
if self.todosData != nil {
return self.todosData!
}
return nil
}
}
I was wondering if all this logic could be done in one single method, maybe using generics?
If you want to force all object to set as a none optional and also get as a none optional, you don't need to define them as optional at the first place:
So instead of:
private var postData : [PostModel]?
you should have:
private var postData = [PostModel]()
This will gives you an empty none optional array and it can not be set or get as an optional.
If you want them to be nil before someone get them (for memory management or etc.), You can make them lazy:
private lazy var postData = [PostModel]()
So now postData will be nil until someone tries to read the value of it.
If you need to do some extra job when someone tries to set one of these, you can observe for changes before set and after set of the value:
private var postData = [PostModel]() {
willSet { /* right before the value is going to set */ }
didSet { /* right after the value is set */ }
}
Note that lazy properties can not have observers
So seems like you don't have any of the functions at all. And you can refactor your code to something like this:
class DataManager : NSObject {
private override init(){}
lazy var postData = [PostModel]()
lazy var userData = [UserModel]()
lazy var commentsData = [CommentsModel]()
lazy var photosData = [PhotosModel]()
lazy var albumsData = [AlbumsModel]()
lazy var todosData = [TodosModel]()
}

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!

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"

iOS Swift: Array of Objects conforming to a Protocol: How to check if array contains

I'm trying to implement an image downloader class. It's a singleton implementation. The idea is to ask the instance to download an image and register as an observer for that image download. This is what I came up with so far:
public protocol ImageDownloaderDelegate {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if (!contains(observerList, delegate)) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}
I get the following error:
'ImageDownloaderDelegate' is not convertible to 'S.Generator.Element -> L'
Any help is much appreciated
You're not passing the correct arguments to the contains function, which expects a collection and a predicate (closure).
You need to do
public protocol ImageDownloaderDelegate : class {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if !contains(observerList!, { observer in observer === delegate }) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}

Resources