I have the following code
class Test: UIViewController {
var imagesOfChef = [Int : chefImages]()
struct chefImages {
var objidChef: String!
var imageChef: UIImage!
}
}
I fill up this dictionary as soon as the user opens the application.
But i want it to be available also in other Views (swift files)
Lets say in this class
class Test2: UIViewController{
}
How can i create a singleton for this Dictionary so it can be available to other views?
Thanks for your time!
You can use a static property:
static var imagesOfChef = [Int : chefImages]()
and then you use:
Test.imagesOfChef
But I suggest avoiding the static approaches as much as possible, you can use the prepare segue or assign the property from outside if possible if Test has Test2.
There are plenty of examples on StackOverlow about how to create a Singleton.
Anyway, your code should be like this
struct Chef {
let id: String
let image: UIImage
}
final class Singleton {
static let sharedInstance = Singleton()
private init() { }
var dict = [Int: Chef]()
}
Now in any source of your app you can use it
Singleton.sharedInstance.dict[1] = Chef(id: "1", image: UIImage())
Related
I have created a Realm object that needs to store an enum value. To do that I use a method outlined in this question which involves declaring a private property of type String, and then declaring another property of type Enum that sets/reads the private property using getters and setters.
For ease of reference here is the code for that:
#objcMembers
class PlaylistRealmObject: Object {
dynamic var id: String = UUID().uuidString
dynamic var created: Date = Date()
dynamic var title: String = ""
private dynamic var revisionTypeRaw: String = RevisionType.noReminder.rawValue
var revisionType: RevisionType {
get { return RevisionType(rawValue: revisionTypeRaw)! }
set { revisionTypeRaw = newValue.rawValue }
}
let reminders = List<ReminderRealmObject>()
let cardsInPlaylist = List<CardRealmObject>()
override static func primaryKey() -> String? {
return "id"
}
}
I have noticed though that if I add a convenience init to the class declaration (to make it a bit easier to initialise the object) the revisionType properties on the objects I end up with adopt the default value declared in the class, and NOT the revision type value passed to the class using the convenience init.
Here is the class declaration with a convenience init
#objcMembers
class PlaylistRealmObject: Object {
dynamic var id: String = UUID().uuidString
dynamic var created: Date = Date()
dynamic var title: String = ""
private dynamic var revisionTypeRaw: String = RevisionType.noReminder.rawValue
var revisionType: RevisionType {
get { return RevisionType(rawValue: revisionTypeRaw)! }
set { revisionTypeRaw = newValue.rawValue }
}
let reminders = List<ReminderRealmObject>()
let cardsInPlaylist = List<CardRealmObject>()
convenience init(title: String, revisionType: RevisionType) {
self.init()
self.title = title
self.revisionType = revisionType
}
override static func primaryKey() -> String? {
return "id"
}
}
And - to make things even more perplexing - if I simply remove the word 'private' from the revisionTypeRaw property, everything works fine!
I am confused. 1) Why does adding a convenience init have this effect? 2) Why does making the property 'public' resolve the issue?
I have created a demo Xcode project to illustrate the issue and can share it if anyone needs it.
Update:
I found the problem. It has nothing to do with the convenience init. I am using #objcMembers at the top of the class as per the Realm docs: https://realm.io/docs/swift/latest/#property-attributes
If you remove this and place #objc in front of the private keyword, everything works as would be expected. I guess the question then is: what explains this behaviour?
This is a good question but I think the issue is elsewhere in the code. Let's test it.
I created a TestClass that has a Realm managed publicly visible var, name, as well as a non-managed public var visibleVar which is backed by a Realm managed private var, privateVar. I also included a convenience init per the question. The important part is the privateVar is being set to the string "placeholder" so we need to see if that is overwritten.
class TestClass: Object {
#objc dynamic var name = ""
#objc private dynamic var privateVar = "placeholder"
var visibleVar: String {
get {
return self.privateVar
}
set {
self.privateVar = newValue
}
}
convenience init(aName: String, aString: String) {
self.init()
self.name = aName
self.visibleVar = aString
}
}
We then create two instances and save them in Realm
let a = TestClass(aName: "some name", aString: "some string")
let b = TestClass(aName: "another name", aString: "another string")
realm.add(a)
realm.add(b)
Then a button action to load the two objects from Realm and print them.
let testResults = realm.objects(TestClass.self)
for test in testResults {
print(test.name, test.visibleVar)
}
and the output:
some name some string
another name another string
So in this case, the default value of "placeholder" is being overwritten correctly when the instances are being created.
Edit:
A bit more info.
By defining your entire class with #objMembers, it exposes your propertites to Objective-C, but then private hides them again. So that property is not exposed to ObjC. To reverse that hiding, you have to say #objc explicitly. So, better practice is to define the managed Realm properties per line with #objc dynamic.
Currently, I've made a custom class called SongNames. Here's the code for it:
import Foundation
class songAttributes {
private var _songTitle: String!
private var _songURL: String!
var songTitle: String {
return _songTitle
}
init(songTitle: String, songURL: String) {
_songURL = songURL
_songTitle = songTitle
}
var songURL: String {
return _songURL
}
}
I have no problem setting values for this class. For instance, I might write:
var songAttribute = songAttributes(songTitle: "That's What I Like", url: "some url")
However, if I try to change the properties of my variable that I just created, like saying:
songAttribute.songTitle = "Locked Out of Heaven"
I get the message: "error: songTitle is a get only property"
That being said, is there a way to mess with my SongName class so that these properties can be changed, and not just get-only?
If you want the properties to be settable then there is no need for the dance with the private properties. Also, the properties should not be implicitly unwrapped, since you are setting them in the initialiser.
import Foundation
class SongAttributes {
var songTitle: String
var songURL: String
init(songTitle: String, songURL: String) {
self.songURL = songURL
self.songTitle = songTitle
}
}
Note that, by convention, class and struct names should start with a capital letter.
You are doing a lot of unnecessary work. You have private variables backing all of your public variables, and have made your public variables computed properties. Get rid of all that. It's needless complexity:
import Foundation
class songAttributes {
var songTitle: String
var songURL: String
init(songTitle: String, songURL: String) {
self.songURL = songURL
self.songTitle = songTitle
}
}
By default properties are read/write, so you don't need to do anything special.
The pattern of having private backing variables that start with the _ prefix is largely a holdover from Objective-C, and rarely needed in Swift.
In Swift I have this Singleton
struct Networking {
static let shared = Networking()
private var observed: Set<String> = []
}
I have to manipulate observed and I need to create useful method to insert and remove member in Set.
mutating func addObserver(for member: String) {
//other code
observed.insert(member)
}
mutating func removeObserver(for member: String) {
//other code
observed.remove(member)
}
The problem is when I try to call this methods like this
Networking.shared.addObserver(for: "x")
because I'm getting this error
cannot use mutating on immutable value: “shared” is a “let” constant
This error is pretty clear. shared is let and obviously it cannot be modified. But to modify the var I need to declare method as mutating. It's a vicious circle.
Any ideas?
Thanks.
If you want your Networking object to act as a singleton, why not make it a class instead of a struct?
class Networking {
static let shared = Networking()
private var observed: Set<String> = []
func addObserver(for member: String) {
//other code
observed.insert(member)
}
func removeObserver(for member: String) {
//other code
observed.remove(member)
}
}
Networking.shared.addObserver(for: "x")
This simplifies the code and solves your issue.
Basically your syntax is wrong, Networking() creates a new instance of the class.
To use the struct as singleton you have to write
Networking.shared.addObserver(for: "x")
Then declare shared as mutable
static var shared = Networking()
There is also another way of doing it
class Networking {
static let shared = Networking()
var observed: Set<String> = [] {
didSet {
print("set has changed")
}
}
}
Value Type
Since Set is a struct (a value type), the didSet block will be executed every time you add or remove and element to Set.
Networking.shared.observed.insert("a")
set has changed
Networking.shared.observed.insert("b")
set has changed
Networking.shared.observed.remove("a")
set has changed
I have two classes where one of them is as a variable in second class. It looks like that:
class Dogs: Object {
dynamic var name: String?
dynamic var age: String?
}
class Person: Object {
dynamic var owner: String?
var dogs: List<Dogs>() //I would like to UPDATE one of element from this list
}
and now, I would like to update one element from this List<Dogs> but I have a still problem with it. I try to do achieve goal doing it:
class ViewController: UIViewController {
var person: Person?
...
func update(){
let updatedDog = Dogs()
updatedDog.name = "Tinky"
updatedDog.age = "12"
try! realm.write {
person.dogs[0] = updatedDog
}
}
}
this solution doesn't work. Do you have any idea how can I update it?
In your ViewController you need to grab the actual Dog you're updating and update it in a write block.
Realm().write {
updatingDog.name = "Tinky"
updatingDog.age = 12
}
// use `updatingDog` as normal
https://realm.io/docs/swift/latest/#updating-objects
Am newbie to swift language. I want to store my login response from a dictionary in singleton class. Whenever i want that data, i want to reuse it from dictionary in singleton class. Please find my mistakes. Herewith i attached my sample code.
My SingleTon Class
class MarblesSingleton: NSObject {
class var sharedInstance:MarblesSingleton
{
struct Singleton
{
static let instance = MarblesSingleton()
}
return Singleton.instance
}
var provisionalDataDict:NSDictionary!
override init() {
super.init()
self.provisionalDataDict = nil
}
}
let singletonClass = MarblesSingleton()
After Login i added that response to dictionary in singleton class like this,
singletonClass.provisionalDataDict = jsonData
In another class, i want to use that data from singleton class like this,
m_CountryTextField.text = NSString(format: "%#", singletonClass.provisionalDataDict.valueForKey("country") as NSString)
My Issue is : i got crash with fatal error.
fatal error: unexpectedly found nil while unwrapping an Optional value
At first you should do what tkanzakic wrote to use the singleton. Then you have to check if your dictionary is valid:
if var unwrapped_Dic = singletonClass.provisionalDataDict {
m_CountryTextField.text = unwrapped_Dic.valueForKey("country") as NSString
}
if you work with optionals, you always should check if the forced unwrapping was successful.
I would do something like this:
class DataManager {
struct Static {
static var token : dispatch_once_t = 0
static var instance : DataManager?
}
class var instance: DataManager {
dispatch_once(&Static.token) { Static.instance = DataManager() }
return Static.instance!
}
init(){
assert(Static.instance == nil, "Singleton already initialized!")
}
var userId = "defaultId"
var password = "defaultPassword"
}
and later on you can just access them by DataManager.Instance.userId
or set to other value DataManager.Instance.userId = "newUserId"
you can also create a Dictionary variable inside the singleton class if you really want.
Hope that help!