How do I create global object in swift? - swift

I think my previous question was too vague. Let me try again.
I'm trying to hold user information by creating a singleton.
After a bit of research, this is what I understood.
Class UserInfo {
var userFirstName: String?
var userLastName: String?
/*I'll add all other variables here*/
}
let sharedInfo = UserInfo()
Am I on right path?

EDIT: For Swift 1.2 and higher (Thanks Will M)
class UserInfo {
static let sharedUserInfo = UserInfo()
var firstName: String?
var lastName: String?
}
For Swift earlier than 1.2
Something along the lines of:
class UserInfo
{
// Singleton Setup
private static var info: UserInfo?
class func sharedUserInfo() -> UserInfo
{
if self.info == nil
{
self.info = UserInfo()
}
return self.info!
}
// Class properties
var firstName: String?
var lastName: String?
}
let user = UserInfo.sharedUserInfo()
user.firstName = "Fred"
user.lastName = "Smith"
I would also recommend making sure that a singleton is really what you want, because depending on how you use it, you could run into some race conditions with threading.

Related

Realm database callbacks

I'm looking at implementing callbacks for some objects in my code. So far the object I have is the following:
class Contact: Object {
#objc dynamic var uid: String? = nil
#objc dynamic var fullName: String? = nil
#objc dynamic var givenName: String? = nil
#objc dynamic var familyName: String? = nil
}
Ideally, I would like to fill fullName once givenName and familyName have a value.
I noticed that Realm has events, but they seem to be pretty broad and I'm not sure that implementing such mechanism will not create more issues than good in my app as I only need to trigger it when the Contact object is being manipulated.
Any suggestion or best practice to share?
What you are looking for are computed properties. However, Realm currently doesn't support storing computed properties, but you don't really need to persist fullName in Realm, since as long as givenName and familyName are persisted, you can always compute fullName.
class Contact: Object {
#objc dynamic var uid: String? = nil
#objc dynamic var givenName: String? = nil
#objc dynamic var familyName: String? = nil
var fullName: String? {
if let givenName = givenName, let familyName = familyName {
return "\(givenName) \(familyName)"
}
return nil
}
}

NSObject setValuesForKeys coding-complaint error in Swift 4.0.2

I usually would create an NSObject like the example below make a firebase observe single event call to get data, then I would create an instance of the NSObject and simply call the setValueForKeys method on the NSObject and the values will be passed in then I could easily use an if let statement to get the specific data I required. this has stopped working since I upgraded to swift 4.0.2 from swift 3.1 see code snippet below. I believe I am doing this wrong way, since the new update. As the key value it requires does exist as can be seen in the NSObject declaration and also the print out. Any suggestion will be appreciated.
class BarberMarker: NSObject {
var companyID: String?
var companyName: String?
var companyLogoImageUrl: String?
var companyLogoImage: UIImage?
var companyFormattedAddress: String?
var companyAddressLongitude: String?
var companyAddressLatitude: String?
var distanceFromCurrentUser: Double?
var dateCreated: String?
var timezone: String?
var calendar: String?
var local: String?
var ratingValue: CGFloat?
}
firebase call to get data from database
func getAllMarkers(){
firebaseRef.child("barberMarkers").observeSingleEvent(of: .value, with: { (snapshotssshhh) in
if let dictionary = snapshotssshhh.value as? [String: AnyObject] {
for marker in dictionary {
if let locMarker = marker.value as? [String: AnyObject] {
var markerB = BarberMarker()
print(locMarker)
markerB.setValuesForKeys(locMarker)
self.barbermarkers.append(markerB)
guard let lon = markerB.companyAddressLongitude, let lat = markerB.companyAddressLatitude else {
return
}
let latitude = (lat as NSString).doubleValue
let longitude = (lon as NSString).doubleValue
let locValue:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
DispatchQueue.main.async{
let desiredMarker = GMSMarker(position: locValue)
desiredMarker.icon = GMSMarker.markerImage(with: UIColor(r: 118, g: 187, b: 220))
desiredMarker.map = self.mapView
self.bMarkers.append(desiredMarker)
}
}
}
}
}, withCancel: nil)
}
error message I get
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<maizewars.BarberMarker 0x7fb62ff5f5b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key companyAddressLongitude.'
try this bro.
#objcMembers class BarberMarker: NSObject {
...
}
reason #objcMembers Use in Swift 4
When a Swift class introduces many new methods or properties that require behavior from the Objective-C runtime, use the #objcMembers attribute in the declaration of that class.
Applying the #objcMembers attribute to a class implicitly adds the #objc attribute to all of its Objective-C compatible members.
Because applying the #objc attribute can increase the compiled size of an app and adversely affect performance, only apply the #objcMembers attribute on declarations when each member needs to have the #objc attribute applied.

Looping Core Data Objects outside a Table/ Collection View

I want to loop through the objects in CoreData without using a tableView or CollectionView... But using a ViewController
I tried something like:
for var i = 0; i < numberOfExerciseItems; i++ {
let exerciseItemsfromDay = fetchedResultController.objectAtIndexPath(i) as! ExerciseItemModel
}
This obviously doesn't work since it is not of NSIndexPath type as you'd get in a table or CollectionView. Are there ways to do this outside a Table / Collection View? Thanks for the ideas in advance.
Edit
From this answer, I'd want to access an entity from an item in the items array in the loop :
let request = NSFetchRequest(entityName: "ExerciseItemModel")
let items = (try? context.executeFetchRequest(request)) as? [ExerciseItemModel] ?? []
In this loop
items.forEach {
print(items) //Displays all the objects in the console.
print(items[1].attribute //Throws the error Value of type'AnyObject' has no member 'attribute' and I cannot access an attribute from the items array . Not sure why!
}
Edit My ExerciseModel Class
import Foundation
import CoreData
#objc(ExerciseItemModel)
class ExerciseItemModel: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
extension ExerciseItemModel {
#NSManaged var exerciseType: String?
#NSManaged var exerciseName: String?
#NSManaged var durationOrSets: String?
#NSManaged var distanceOrReps: String?
#NSManaged var weight: String?
#NSManaged var backgroundImage: NSData?
#NSManaged var dayID: String?
#NSManaged var date: NSDate?
}
let request = NSFetchRequest(entityName: "ExerciseItemModel")
let items = (try? context.executeFetchRequest(request)) as? [ExerciseItemModel] ?? []
items.forEach {
// Do your stuff
}
A small example. Not exactly an answer so I'll delete it when a proper answer has been posted. This example will not show you any error messages if there are any, just keep that in mind.
Edit:
You're using forEach() incorrectly:
items.forEach {
print($0) // Print's the object in items
print($0.attribute) // Should print the attribute
}
print(items[0].attribute) // Probably same error, but it should be used outside the loop

Saving custom object as core data property

I am trying to save a custom class as a property of a core data object.
The Core Data object:
#objc(Safer)
class Safer: NSObject
{
#NSManaged var perakimFiles: [prayer]? //This line gives me an error saying that it cannot save a type that is not Obj. C
#NSManaged var titleEnglish: String?
#NSManaged var titleHebrew: String?
#NSManaged var parshaPerakim: [Int]?
#NSManaged var saferCode: String?
var titles: [[String]]
{
get
{
var ret = [[String]]()
var i = 0
for file in perakimFiles!
{
ret.append([file.title, "\(i+1)"])
i++
}
return ret
}
}
init(_ _saferCode: SefarimCodes)
{
super.init()
self.saferCode = _saferCode.rawValue
}
init(_perakimFiles: [prayer], _titleEnglish: String, _titleHebrew: String)
{
super.init()
self.perakimFiles = _perakimFiles
self.titleEnglish = _titleEnglish
self.titleHebrew = _titleHebrew
}
init(_perakimFiles: [prayer], _titleEnglish: String, _titleHebrew: String, _parshaPerakim: [Int])
{
super.init()
self.perakimFiles = _perakimFiles
self.titleEnglish = _titleEnglish
self.titleHebrew = _titleHebrew
self.parshaPerakim = _parshaPerakim
self.saferCode = setTorahSaferCode()!.rawValue
let config = self.configFromCode()
self.perakimFiles = config.perakimFiles
}
}
Here is the prayer class that I am trying to save in the core data object:
class prayer
{
var url: NSURL
var title: String
var detail: String?
init(initURL: NSURL, initTitle: String)
{
print("1")
print("2")
self.title = initTitle
print("3")
self.url = initURL
print("4")
}
init(initURL: NSURL, initTitle: String, initDetail: String)
{
self.url = initURL
self.title = initTitle
self.detail = initTitle
}
}
So what can I do to the prayer class to make it savable by the core data object? I need to also be able to use instances of the prayer class in other places of the code.
As mentioned, have your prayer class subclass NSObject and conform to NSCoding which requires two methods : -initWithCoder: and encodeWithCoder:
Once those are implemented, you can use NSKeyedArchiver/NSKeyedUnarchiver class methods to transform your objects into NSData objects and back, thus allowing you to store your objects as CoreData properties under the supported NSData type.
Let class prayer conforms to NSObject and NSCoding and see whether that addresses the error.

Swift Struct Identity

I have a following struct which defines a shoppingList.
struct ShoppingList {
var shoppingListId :NSNumber
var title :String
}
let shoppingList = ShoppingList(shoppingListId: NSNumber, title: String) // I don't want to assign shoppingListId
Now, the problem is that it auto-generates the constructors which takes in the shoppingListId as parameter. Since shoppingListId is an identity or unique key I don't want to pass in with the constructor. It will be set later by the ShoppingListService after inserting shoppingList into the database.
Is this a place where class will make more sense than structures?
UPDATE:
struct ShoppingList {
var shoppingListId :NSNumber
var title :String
init(title :String) {
self.title = title
// but now I have to assign the shoppingListId here unless I make it NSNumber? nullable
}
}
let shoppingList = ShoppingList(title: "Hello World")
UPDATE 3:
let shoppingList = ShoppingList()
// THE FOLLOWING CODE WILL NOT WORK SINCE IT REQUIRES THE shoppingList to // BE var instead of let
shoppingList.shoppingListId = NSNumber(int: results.intForColumn("shoppingListId"))
shoppingList.title = results.stringForColumn("title")
shoppingLists.append(shoppingList)
If you don't want the default constructor, then make one of your own. However, it is important to keep in mind that unless your property is an Optional, it has to be initialized to some value. In this case, you will have to give shoppingListId a value, or you will need to make it an Optional. Also, I don't see any reason why a class would be better than a struct for this scenario.
struct ShoppingList {
var shoppingListId: NSNumber
var title: String
init(title: String) {
self.title = title
shoppingListId = NSNumber(integer: 0)
}
}
let newList = ShoppingList(title: "Groceries")
Update:
Just saw you updated your question. If you are not able to pick a reasonable initial value for your shoppingListId, then make it an Optional like so:
struct ShoppingList {
var shoppingListId: NSNumber?
var title: String
init(title: String) {
self.title = title
}
}
let newList = ShoppingList(title: "Groceries")
Just realize shoppingListId will be nil till you set it to something, and everywhere you use it you should bind the value in an if let.