I'm learning about Xcode and using tutorials. I'm a beginner.
I'm looking at this code and have no clue why this is coming up with the following error:
Use of unresolved identifier 'USER_REF'
but in the code I'm pretty sure I reference the USER - is that right?
class DataService {
static let dataService = DataService()
fileprivate var _BASE_REF = Database.database().reference()
fileprivate var _USER_REF = Database.database().reference()
fileprivate var _USERPOST_REF = Database.database().reference()
var BASE_REF: DatabaseReference {
return _BASE_REF
}
var USER_REF: DatabaseReference {
return _USER_REF
}
var CURRENT_USER_REF: DatabaseReference {
let userID = UserDefaults.standard.value(forKey: "uid") as! String
let currentUser = Database.database().reference().child(byAppendingPath: "user").child(byAppendingPath: userID)
return currentUser
}
var USERPOST_REF: DatabaseReference {
return _USERPOST_REF
}
}
func createNewAccount(uid: String, user: Dictionary<String, String>) {
// A User is born?
USER_REF.child(byAppendingPath: uid).setValue(user)
}
func createNewUserPost(userpost: Dictionary<String, AnyObject>) {
// Save the Post
// USERPOST_REF is the parent of the new USERPOST: "userposts".
// childByAutoId() saves the userpost and gives it its own ID.
let firebaseNewUserPost = USERPOST_REF.childByAutoId()
// setValue() saves to Firebase.
firebaseNewUserPost?.setValue(userpost)
}
This is a perfect lesson in why indentation is important. There's a second } after the end of USERPOST_REF: DatabaseReference, which ends the scope of the class DataService.
As a result, createNewAccount(uid:user:) and createNewUserPost(userpost:) are standalone functions without access to any instance members of DatabaseReference.
A few points of improvement:
Swift convention for all properties, instance, class or static, is to use lowerCamelCase.
Unlike other languages (Java and Ruby are the main offenders here that I know of), there is no point making an instance variable with a public getter (computed property), such as filprivate _x: Int, with a public var x: Int. Namely, because _x is not an instance variable. Swift doesn't actually let you make instance variables. They're generated for you when you create properties. What you're doing is writing a property that accesses a property you declared, to access an instance variable the compiler synthesized. There's no need for this. Just make the property's setter scoped to file private.
The conventional name of a singleton in Swift is shared, default or main. Try to stick with one of those. Additionally, in order for you to truly have a singleton, you need to restrict access to the initializer, by declaring it with private access.
Here is what I would recommend:
class DataService {
static let shared = DataService()
private init() {}
public fileprivate(set) var baseDB = Database.database().reference()
public fileprivate(set) var userDB = Database.database().reference()
public fileprivate(set) var userPostDB = Database.database().reference()
var currentUser: DatabaseReference {
let userID = UserDefaults.standard.value(forKey: "uid") as! String
return Database.database()
.reference()
.child(byAppendingPath: "user")
.child(byAppendingPath: userID)
}
func createNewAccount(uid: String, user: Dictionary<String, String>) {
// A User is born?
userDB.child(byAppendingPath: uid).setValue(user)
}
func createNewUserPost(userpost: Dictionary<String, AnyObject>) {
// Save the Post
// userPostDB is the parent of the new USERPOST: "userposts".
// childByAutoId() saves the userpost and gives it its own ID.
let firebaseNewUserPost = userPostDB.childByAutoId()
// setValue() saves to Firebase.
firebaseNewUserPost?.setValue(userpost)
}
}
Related
I have created a class with some variables and have not initialized with any default value, all variables should be assigned when web service call occurs. So I have initialized the instance of the class and assigned the value to the variables.
Here I want to access these values to all class file throughout the project. Is it possible? I do not want to use any saving methods like core data and user defaults also codable local storage.
Please help me out with this? We tried to access the model class value in another view controller. But we get a nil value. Thanks in Advance.
//MARK: Shared Instance
static let sharedInstance = Singleton()
//MARK: Local Variable
var emptyStringArray : [String]? = nil
var completed : Bool!
var id : Int?
var title : String?
var userId : Int?
//MARK: Init Array
private init() {
}
init(Fromarray dictionary : [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
finally called in
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let BoolValue = Singleton.sharedInstance.completed
print(BoolValue)
}
This is a very poor architectural decision.
Among the many the first problem here is that results of API calls are always asynchronous so you can't tell if your class have been properly initialized at the moment you use it's properties.
You create too much ambiguity by creating forced unwrapped optional that depends on network call. What if API call fails? What if internet connection is slow, how would you predict that your singleton is "fine" at this moment? For most of the cases you will be accessing nil and it will crash your app.
I'd suggest more reading on Singleton pattern (if it's necessary) and also on architectural patterns in iOS.
use different function to load data and call that after WebCall
func loadData(Fromarray dictionary : [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
call after web call completion
Singleton.sharedInstance.loadData(FromArray : YourDictoinary)
and acess anywhre throught project
let title = Singleton.sharedInstance.title
You need to keep a single object of your singleton class.
static let sharedInstance = Singleton()
It will create a object of your singleton class.
private init() { } or,
init(fromarray dictionary: [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
It will create another object of your singleton class, which will be differ form above.
If you need to access the data from your singleton class. Create your class like:
class Singleton {
static let sharedInstance = Singleton()
var emptyStringArray: [String]?
var completed: Bool?
var id: Int?
var title: String?
var userId: Int?
func initializeData(_ dictionary: [String:Any]) {
completed = dictionary["completed"] as? Bool
id = dictionary["id"] as? Int
title = dictionary["title"] as? String
userId = dictionary["userId"] as? Int
}
}
And use it like:
override func viewDidLoad() {
super.viewDidLoad()
// Initialize with data
Singleton.sharedInstance.initializeData(["completed": true, "id": 123, "title": "iOS Title", "userId": 572])
// Access data
let boolData = Singleton.sharedInstance.completed
print(boolData)
}
import RealmSwift
import Realm
public class Card : Object {
var username : String
var firstName : String
var lastName : String
init?(dictionary: [String:Any]?) {
guard let dictionary = dictionary , let username = dictionary["username"] as? String else { return else}
self.username = username
self.firstName = firstName
self.lastName = lastName
}
required public init() {
fatalError("init() has not been implemented")
}
required public init( realm: RLMRealm, schema: RLMObjectSchema) {
fatalError("init(realm:schema:) has not been implemented")
}
required public init( value: Any, schema: RLMSchema) {
fatalError("init(value:schema:) has not been implemented")
}
}
I get:
'self' used before super.init call
I had my class working properly. After adding RealmSwift i'm getting those errors. If I add super.init() it complains:
Property 'self.username' not initialized at super.init call
Because your properties are String.
From the apple docs...
Setting Initial Values for Stored Properties
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition. These actions are described in the following sections.
You have two options:
1)
var username : String = ""
var firstName : String = ""
var lastName : String = ""
2)
var username : String?
var firstName : String?
var lastName : String?
There's a couple of things going on here.
First and foremost, when adding custom initializers to subclasses of Object, they must be declared as convenience initializers. It's not possible to correctly implement Object's required initializer from a subclass, and using a convenience initializer removes the need to try and do this. It also means you'll instead delegate to self.init() rather than super.init() from within your custom initializer.
Secondly, stored properties must have an initial value. Without the initial value the Swift compiler will not synthesize initializers for your class (in this case, a version of init() that calls through to the base class).
Finally, as I mentioned elsewhere, properties of type String must be declared using Swift's dynamic modifier to allow Realm to intercept get / set operations on them.
By following these guidelines you'll end up with something like so:
public class Card : Object {
dynamic var username: String = ""
dynamic var firstName: String = ""
dynamic var lastName: String = ""
convenience init?(dictionary: [String:Any]?) {
guard let dictionary = dictionary,
let username = dictionary["username"] as? String,
let firstName = dictionary["firstName"] as? String,
let lastName = dictionary["lastName"] as? String
else { return nil }
self.init()
self.username = username
self.firstName = firstName
self.lastName = lastName
}
}
I Have one class UserObject(Object) that has one property of SessionObject(Object), and the SessionObject has properties of other Realm Objects(TestObject, NewObject).
When I delete the children Objects and after that the parent Object successfully, the app crashes with Bad Access: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
class UserObject: Object{
dynamic var uuid = ""
dynamic var username = ""
dynamic var session: SessionObject?
class SessionObject: Object{
dynamic var tokenType = ""
dynamic var refreshToken = ""
dynamic var test: TestObject?
dynamic var new: NewObject?
}
class TestObject: Object{
dynamic var test = ""
}
class NewObject: Object{
dynamic var test = ""
}
func deleteUser(){
guard let realm = self.realmInstance else{
return
}
guard let user = Array(realm.objects(UserObject.self)).first else{
return
}
do{
try realm.write {
if let session = user.session{
if let test = session.test{
realm.delete(test)
}
if let new = session.new{
realm.delete(new)
}
realm.delete(session)
}
realm.delete(user)
}
}catch{
}
}
You're running in to ARC naming conventions, which apply to dynamic properties on Swift classes. Currently Realm does not generate accessor methods which conform to what ARC expects for specially-named methods, which results in objects being double-deleted when you have a property name starting with new, copy or mutableCopy.
I have an array of struct elements that I would like to store globally so that I can access it in different classes without having to run the query that populates it over and over.
I have a struct:
struct collectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
and a variable:
var collectionArray = [collectionStruct]()
and some code to build the array:
for object in items {
let arrayName = object.object(forKey: fromName) as! String
let arrayDescription = object.object(forKey: fromDescription) as! String
let arrayTitle = object.object(forKey: fromTitle) as! String
let arrayImage = object.object(forKey: fromImage) as! PFFile
let arrayID = object.objectId as String!
collectionArray.append(collectionStruct(name: arrayName,
description: arrayDescription,
title: arrayTitle,
image: arrayImage,
id: arrayID!))
}
I was thinking of creating another struct to hold the array itself bt am a bit lost here. this is what I was thinking:
struct globalArray {
var collectionArray = [collectionStruct]()
}
but a probably way off
You should consider naming your struct CollectionStruct rather than collectionStruct - as that indicates it is a type. To access the array anywhere you could create a singleton. With a singleton, you ensure there is only one instance available by giving it a private constructor. Here is an example:
class Global {
private init() { }
static let sharedInstance = Global()
var collection = [CollectionStruct]()
}
To use it you would use the following:
Global.sharedInstance.collection
You can use singleton for global class that's able to be accessing from anywhere:
class GlobalArray {
static let shared = GlobalArray()
var collectionArray = [collectionStruct]()
}
and accessing like this to assign or read value:
GlobalArray.shared.collectionArray
You can just declare
var collectionArray = [collectionStruct]()
at the top level in any file (outside any object), and it will be available globally
e.g.
var collectionArray = [collectionStruct]()
class MyClass {
func printArray() {
print(collectionArray)
}
}
I am trying to create an AppSettings class to store user setttings in an user dictionary. My AppSettings class:
class AppSettings : NSObject
{
static var user: [String: Any]?
static let sharedSingleton : AppSettings = {
let instance = AppSettings()
return instance
}()
var userID: String?
var baseUrl: String?
override private init() {
super.init()
let userDefaults = UserDefaults.standard
AppSettings.user = userDefaults.object(forKey: "user") as! [String : Any]?
print(AppSettings.user ?? "User Defaults has no values")
self.saveSettings()
}
func saveSettings()
{
let userDefaults = UserDefaults.standard
if((AppSettings.user) != nil)
{
userDefaults.set(AppSettings.user, forKey: "user")
}
userDefaults.synchronize()
}
}
I am storing values in user this way:
AppSettings.user?["User_ID"] = String(describing: dictionary["User_ID"])
AppSettings.sharedSingleton.saveSettings()
let defaults = UserDefaults.standard.object(forKey: "user")
print(defaults ?? "User is null")
It shows that User is null. What's wrong?
Assuming your example is complete, the problem lies in this line
AppSettings.user?["User_ID"] = String(describing: dictionary["User_ID"])
The user variable is an optional, so unless you created an empty dictionary first this is what happens - if user is not nil, assign a value under "User_ID" key.
All you need to do is write AppSetting.user = [String:Any]() before trying to add values to it. Now, it may seem that you do it in init(), but it's not called unless you call sharedInstace.
On a side note - if you are making this a singleton, why do you expose the user variable as a static?