Unresolved Identifier "itemID" - ios

How can I Globally define (barcode scanned) so that it can be accessible by all my functions. In order words how can I define “metadataObj” globally?
class ScanController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
//Get an instance of the AVCaptureDevice class a device object and provide the video as the media type parameter
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
do {
// Get an instance of the AVCaptureDeviceInput class using the previous device object.
let input = try AVCaptureDeviceInput(device: captureDevice)
// Initialize the captureSession object.
captureSession = AVCaptureSession()
// Set the input device on the capture session.
captureSession?.addInput(input)
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
//initialize QR Code Frame to highlight the QR Code
qrCodeFrameView = UIView()
if let qrCodeFrameView = qrCodeFrameView {
qrCodeFrameView.layer.borderColor = UIColor.green.cgColor
qrCodeFrameView.layer.borderWidth = 2
view.addSubview(qrCodeFrameView)
}
} catch {
// If any error occurs, simply print it out and don't continue any more.
print(error)
return
}
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No QR/barcode is detected"
return
}
//Get metadata object
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
//if the found metadata is equal to the QR code metadata then update the status label's text and set the the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
//Searches firebase for existing barcode
}
let itemToSearchFor = metadataObj.stringValue
let itemID = metadataObj.stringValue
guard let Description = productDescriptionTextField.text,
let price = priceTextField.text,
let location = productLocationTextField.text
else{
print("Fill basic product information")
return
}
let ref = FIRDatabase.database().reference(fromURL: " /")
// creating an item child node
let values = ["Item Description": Description, "Image": price, "Location": location, "Price": price ]
let items = ref.child("Items").child(itemID!)
items.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err)
return
} })
FIRDatabase.database().reference().child("Items").child(itemToSearchFor!).observeSingleEvent(of: .value, with:{(snap) in
print(snap)
})
self.setupNewProductEntry()
self.setupenterNewProductButton()
}
}
I have one error when I tried to use ItemID within another function. I think this is because it is not globally defined, it is only defined within func captureOutput. Any ideas on how to globally define the barcode string values that I get from my barcode scanner?

You can move it beyond your class scopes, so it'll become global and visible through all your module

Declare it up here (where the comment is) to make it accessible.
class ScanController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeFrameView: UIView?
//DECLARE itemID here
override func viewDidLoad() {
super.viewDidLoad()
//Get an instance of the AVCaptureDevice class a device object and provide the video as the media type parameter
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

Just put "metadataObj" outside the class in the global scope (beginning of the file after the import section, I mean, outside the class declaration), so that it isn't a member of the class anymore (and initialize it with a value of your choice). If you declare it in a framework and want to use it in your app, then add the public access modifier, or you won't be able to access it.
If you want to put it in the scope of the class but accessible from everywhere in your code, add the static modifier (or static public if you declare it in a framework). Using static you'll create a single instance of the variable, not related to any particular instance of your class (one and only one instance of metadataObj shared among all instances of your class). Be careful if you access metadataObj from different threads... you could face very annoing faults, and difficult to fix. In this latter case consider thread synchronization (not exactly an easy subject to cope with)... I would discourage that if you haven't a very clear idea about what you need to do. If you, instead, need it only within the class scope (I mean, to be used by all the member functions... and only them), put it where Alec told you.

Related

Scanning QR code crashes often - AVCaptureSession [duplicate]

This question already has an answer here:
Swift: Unable to detect linear type Barcodes
(1 answer)
Closed 4 years ago.
Am trying to scan QR code with the below code and it works fine. But sometimes it crashes with the following error.
Could not cast value of type 'AVMetadataFaceObject' (0x1b245bd28) to 'AVMetadataMachineReadableCodeObject' (0x1b245be68). Help much appreciated.
P.S: Am showing camera inside tabbar controller
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No QR code is detected"
return
}
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
// If the found metadata is equal to the QR code metadata (or barcode) then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
launchApp(decodedURL: metadataObj.stringValue!)
messageLabel.text = metadataObj.stringValue
captureSession?.stopRunning()
captureSession = nil
}
}
}
The use cases of a force cast are very limited, it is almost never a good idea. You should use:
guard let metadataObj = metadataObjects[0] as? AVMetadataMachineReadableCodeObject else{
// Display some sort of error message or handle it
return
}
Make sure you are detecting QR codes and not faces:
captureMetadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr]

Referring to my view controller in another class (scene.swift) - all variables turn to null? Swift 3

I am trying to call a method from my view controller in my Scene.swift class - which I am able to do. The method is called after the touchesBegan method is called when I click on an SKLabel node, which are set up in my view controller.
The problem is that when I click on an SKLabelNode, control passes to the Scene.swift class and within the touchesBegan method, the method I want to call is called, so the control is passed back to the view controller, when I get back here it seems that all my variables are set to nil, as if its a completely different instance of the controller?
The error occurs in the checkIfValidTime method when I try to set the text property of a label in the ArViewController. - I highlighted these lines with **.
Error:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
How do I reference the same instance of the view controller so that the variables don't reset when I declare it in the Scene.Swift?
Or is there a way I can implement the touchesBegan method in the view controller so that I don't have to instantiate the ARViewController?
I would appreciate any help on this matter as I have been stuck for a while now on it and I am new to iOS and swift app design.
I have tried to limit the code to what is necessary for me to explain this problem.
Any questions just ask. Thanks
ARViewController:
public var receivedCallback : Bool = false
class ARViewController: UIViewController, ARSKViewDelegate, URLSessionDelegate {
// MARK: - IBOutlets
#IBOutlet weak var sceneView: ARSKView!
#IBOutlet weak var guideLabel: UILabel!
#IBOutlet weak var testLbl: UILabel!
var scene : Scene?
static var dateToUse : Date?
var aRLocalDate : Date?
var button: SKSpriteNode?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
/*
Start the view's AR session with a configuration that uses the rear camera,
device position and orientation tracking, and plane detection..
*/
let configuration = ARWorldTrackingConfiguration()
guard ARWorldTrackingConfiguration.isSupported else {
fatalError(""
ARKit is not available on this device."")
}
sceneView.session.run(configuration)
sceneView.delegate = self
if let scene = SKScene(fileNamed: "Scene"){
self.scene = scene as! Scene
sceneView.presentScene(self.scene)
} else {
print("Error: scene initalisation failed")
}
let overflow = ((aRLocalDate?.debugDescription.count)! - 11)
let endIndex = aRLocalDate?.debugDescription.index((aRLocalDate?.debugDescription.endIndex)!, offsetBy: -overflow)
if let truncatedDate = aRLocalDate?.debugDescription.substring(to: endIndex!){
DateLabel.text = truncatedDate
}
}
**/// - Tag: PlaceARContent**
func view(_ view : ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
if self.Generated == false{
self.guideLabel.alpha = 0
parentNode = SKShapeNode(rectOf: CGSize(width: 400, height: 720))
var count = 1;
for time in timesArray {
**//add a SKSpriteNode and assign image to it**
**let labelNode : SKLabelNode = SKLabelNode(text: time)**
labelNode.name = "booklabel" + String(count)
labelNode.isUserInteractionEnabled = false;
parentNode?.addChild(labelNode)
posy -= 60
count += 1
}
parentNode?.alpha = 0.6
self.Generated = true
drawEventNodes()
return parentNode
}
else {
return nil
}
}
//check if the booking is not in the past
func checkIfValidTime(bookingTime: String, startDateTimeDate: Date) -> Bool {
thisDate = ARViewController.dateToUse;
let date = Date()
let currentHour = Calendar.current.component(.hour, from: date)
if (startDateTimeDate > date) {
print("Start time is greater than the current date. = valid")
**self.guideLabel.text = "Test"**
return true;
}
else {
print("Start time is not valid - before current time")
**self.guideLabel.text = "Test"**
return false;
}
}
func doPost(bookingTime: String) {
print("Start of post method")
thisDate = ARViewController.dateToUse;
roomToBook = globalVariables.roomDictionary[globalVariables.userString]!
let name = globalVariables.userString;
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let displayName = globalVariables.userString
let startDateStr = dateFormatter.string(from: thisDate!)
let startHourString = bookingTime
print("StartDateStr:", startDateStr)
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let startDateTimeString = "\(startDateStr)T\(startHourString)"
let startDateTimeDate = dateFormatter.date(from: startDateTimeString)
let endDateTimeDate = startDateTimeDate?.addingTimeInterval(3600)//3600 = 1 hour
let endDateTimeString = dateFormatter.string(from: endDateTimeDate!)
print("Start Date Time String", startDateTimeString)
print("End date time string", endDateTimeString)
print ("room to book: ",roomToBook)
let valid = checkIfValidTime(bookingTime: bookingTime, startDateTimeDate: startDateTimeDate!)
if (valid == true) {
let jsonObject: [String: Any] =
[
"subject": "Booking",
"body":[
"contentType": "HTML",
"content": "Test Booking"
],
"start":[
"dateTime": startDateTimeString,
"timeZone": "UTC"
],
"end": [
"dateTime": endDateTimeString,
"timeZone": "UTC"
],
"location":[
"displayName": displayName
],
"attendees":[[
"emailAddress": [
"address": roomToBook,
"name": displayName
],
"type": "required"
]]
]
//let valid = JSONSerialization.isValidJSONObject(jsonObject) // true
let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject)
// create post request
let url = URL(string: "https://graph.microsoft.com/v1.0/me/events")!
var request = URLRequest(url: url)
request.setValue("Bearer \(globalVariables.accessToken)", forHTTPHeaderField: "Authorization")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept") // the expected response is also JSON
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
print("Post Done")
print("Refreshing now")
//code to refresh?
}
else {
print("Invalid booking time - it is in the past.")
}
}
Scene.Swift:
class Scene : SKScene{
var controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ARStoryBoard") as! ARViewController
// var controller: ARViewController!
var bookingTime : String?
override func touchesBegan(_ touches: Set<UITouch>, with event : UIEvent?) {
// var c = self.view?.window?.rootViewController as! ARViewController;
for touch in touches {
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
let name = node.name
switch name {
case "booklabel1"?:
controller.doPost(bookingTime: "08:00:00")
case "booklabel2"?:
controller.doPost(bookingTime: "09:00:00")
case "booklabel3"?:
controller.doPost(bookingTime: "10:00:00")
case "booklabel4"?:
controller.doPost(bookingTime: "11:00:00")
case "booklabel5"?:
controller.doPost(bookingTime: "12:00:00")
case "booklabel6"?:
controller.doPost(bookingTime: "13:00:00")
case "booklabel7"?:
controller.doPost(bookingTime: "14:00:00")
case "booklabel8"?:
controller.doPost(bookingTime: "15:00:00")
case "booklabel9"?:
controller.doPost(bookingTime: "16:00:00")
case "booklabel10"?:
controller.doPost(bookingTime: "17:00:00")
case "booklabel11"?:
controller.doPost(bookingTime: "18:00:00")
default:
print ("No Specific Label Clicked")
}
}
}
}
Try This. (Check updated Edited answer: Highly recommended)
ARViewController: Create an instance outside the class
weak var arViewControllerInstance = ARViewController()
Make sure initialization inside ARViewController class:
override func viewDidLoad() {
super.viewDidLoad()
arViewControllerInstance = self
}
Now you can call in Scene.Swift using:
arViewControllerInstance?.doPost(bookingTime: "08:00:00")
Edited: Highly recommended
Above Method Simple but highly not recommended for best practice. Have a look below implementation using delegate protocol.
Create a protocol
protocol ScenceArViewControllerDelegate {
func doPost(bookingTime: String)
}
Add Above delegate in ARViewController class as below.
class ARViewController: UIViewController, ScenceArViewControllerDelegate{
func doPost(bookingTime: String){
//Funcion body goes here
}
}
Create a delegate variable inside the Scene class as below (Scene.swift)
class Scene: SKScene{
weak var delegateARVC: ScenceArViewControllerDelegate?
}
Once you implement above all. Now you have declared the delegateARVC variable from ARViewController as below code. (Note: You can use dependency injection for set value but below just sets object)
class ARViewController: UIViewController, ScenceArViewControllerDelegate{
var scene: Scene?
override func viewDidLoad() {
super.viewDidLoad()
scene = Scene()
scene.delegateARVC = self
}
func doPost(bookingTime: String){
//Funcion body goes here
}
}
Now all good. Now your Scene class knows it has a relationship reference with ARViewController by the method called doPost using ScenceArViewControllerDelegate.
You can call AARViewController 's doPost method as below from Scene class.
guard let delegateARCAvailable = delegateARVC else { return }
delegateARCAvailable.doPost(bookingTime: "08:00:00")
You can apply as you want. Thanks.
#Liam There is 'chain of ownership'.
"Own" mean take responsibility for keeping object alive. Example Library, Reader and Book. Book had stamp of Library so it has reference to it, but definitely Book do not own Library. Same for reader. But both Library and Reader claims ownership of the book. If nobody take Book to Read declaring ownership) , and library is destroyed - them book is destroyed as well.
In Swift it is implemented via strong and weak references. All variables are strong by default.
Usually you view controllers instantiated by app delegate, or by other view controllers (manually or using storyboard). Anyone else can be used by view controllers, and can be aware about viewcontroller but do not "own" it. So variable had to be declared as weak. Mind that you don't own weak, so it had to be optional and can be nullified at any moment (if user navigate back), so you had to use additional guard.
To summarise:
1: Declare your viewController in the scene as weak:
class Scene : SKScene{
weak var controller: ARViewController?
....*
}
2: Supply your scene with view controller. You can do it when creating Scene, or when setting it. For example:
class ARViewController: UIViewController, ARSKViewDelegate, URLSessionDelegate {
//...
var scene : Scene? {
didSet{
//Optional: In case you can change scenes - remove view controller from old scene
oldValue?.controller = nil
//Actually set view controller of any scene it "own"
scene?.controller = self
}
}
PS: there is additional modifier "unowned". But it is more advance technique, can cause issues and crashes. I advice you to make yourself familiar with weak. Get used to guard retain cycles and then proceed.

Swift: Change UITextField Value from inside NSItemProvider

I've been battling this one all day, as you can see I'm new to Swift.
I'm trying to update a UITextField in a iOS Action Extension. Correct value logs below, and outside of the loadItem() the correct value can be set.
Weak variable in the outlet? Some sort of closure thing? Function fires asynchronously and I'm not allowed to update the UI this way once it finishes?
Thank you in advance!
class ActionViewController: UIViewController {
//outlets
#IBOutlet weak var bookmarkTitle: UITextField!
//vars
var pageTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
if let inputItem = extensionContext!.inputItems.first as? NSExtensionItem {
if let itemProvider = inputItem.attachments?.first as? NSItemProvider {
itemProvider.loadItem(forTypeIdentifier: kUTTypePropertyList as String) { [unowned self] (dict, error) in
// do stuff!
let itemDictionary = dict as! NSDictionary
let javaScriptValues = itemDictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
print(javaScriptValues)
//this works, gets correct data. how to assign to IBOutlet?
self.pageTitle = javaScriptValues["title"] as! String
NSLog("Title From JS Preprocessor: " + self.pageTitle)
/*===================================*/
//Reference to property 'bookmarkTitle' in closure requires explicit 'self.' to make capture semantics explicit
//bookmarkTitle.text = self.pageTitle
//when the line below is used, the action extension fails to even open
self.bookmarkTitle.text = self.pageTitle
/*===================================*/
}
}
}
}
}
try to change the textfield text in Main Queue like this:
DispatchQueue.main.async {
// work that impacts the user interface
self.bookmarkTitle.text = self.pageTitle
}

how to make a value inside a function global

I would like to make the value of ItemID global so that I can access it out side the function. I have attempted to define it right below class but it doesn't work. How else can I make a value inside a function accessible by other functions?
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No QR/barcode is detected"
return
}
//Get metadata object
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
//if the found metadata is equal to the QR code metadata then update the status label's text and set the the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
//Searches firebase for existing barcode
}
let itemID = metadataObj.stringValue!
let itemToSearchFor = itemID
guard let Description = productDescriptionTextField.text,
let price = priceTextField.text,
let location = productLocationTextField.text
else{
print("Fill basic product information")
return
}
let ref = FIRDatabase.database().reference(fromURL: "/")
// creating an item child node
let values = ["Item Description": Description, "Image": price, "Location": location, "Price": price]
let items = ref.child("Items").child(itemID)
items.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err)
return
} })
FIRDatabase.database().reference().child("Items").child(itemToSearchFor).observeSingleEvent(of: .value, with:{(snap) in
print(snap)
})
}}
This is the function where I want to call itemID
func enterNewProduct() {
guard let Description = self.productDescriptionTextField.text,
let price = self.priceTextField.text,
let location = self.productLocationTextField.text
else{
print("Fill basic product information")
return
}
let ref = FIRDatabase.database().reference(fromURL: "/")
// creating an item child node
let values = ["Item Description": Description, "Image": price, "Location": location, "Price": price ]
let items = ref.child("Items").child(itemID)
items.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err)
return
} })
self.dismiss(animated: true, completion: nil)
}
for some reason it is not recognizing the itemID from the previous function
Put it above the function somewhere like this.
var itemID:String!
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
Then in your function you should be able to access it like this.
itemID = metadataObj.stringValue!
let itemToSearchFor = itemID
Make sure to use var so that you can modify it.
It sounds like you've done everything correctly. You forgot to give the code where you "attempted to define it right below class". In case that's the issue, here a template that works:
class ViewController: UIViewController {
var myID = "ID #1"
override func viewDidLoad() {
super.viewDidLoad()
changeID()
printID()
}
func changeID() {
printID()
myID = "ID #2"
}
func printID() {
print(myID)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The console output is:
ID #1
ID #2

Init method for a singleton

I have my file myAPI.swift, and two objet Round and GameStats. My Round object also have an attribute GameStats. So what I want to do is to get the GameStats property that I store inside my users defaults then assign it inside my Round object.
class myAPI: NSObject {
static let sharedInstance = myAPI()
var currentStats: GameStats?
var currentRound: Round?
private init(){
super.init()
self.loadData()
NSLog("Stats have been reload: \(self.currentRound?.gameStats)") // Return nil
// If I try to add this line the app stop running and nothing happens
NSLog("Test Singleton: \(myApp.sharedInstance.currentRound?.gameStats)")
}
func loadData(){
let backupNSData = NSUserDefaults.standardUserDefaults().objectForKey("backupNSData")
if let backupNSData = backupNSData as? NSData{
let backupData = NSKeyedUnarchiver.unarchiveObjectWithData(backupNSData)
if let backupData = backupData as? [String:AnyObject] {
guard let round = backupData["currentRound"] as? Round else {
print("error round loaddata")
return
}
self.currentRound = round
guard let stats = backupData["stats"] as? GameStats else {
print("error guard stats")
return
}
self.currentRound.gameStats = stats
NSLog("Stats reloaded: \(stats)") // This is not nil it works here
}
}
}
When my app crash I call this function to save the data
func backupData(){
var backupData:[String:AnyObject] = [String:AnyObject]()
if let round = self.currentRound {
backupData["currentRound"] = round
ColorLog.purple("Stats saved inside Round \(round.gameStats)")
}
if let stats = self.currentStat {
backupData["stats"] = stats
ColorLog.purple("Stats saved : \(stats)")
}
let backupNSData = NSKeyedArchiver.archivedDataWithRootObject(backupData)
NSUserDefaults.standardUserDefaults().setObject(backupNSData, forKey: "backupNSData")
NSUserDefaults.standardUserDefaults().synchronize()
}
So I have two question,
Is it normal that I can't call my singleton like myApp.sharedInstance.currentRound.id = 5 (for instance) inside the init() (I guess it is but I didn't find anything about that)
Why in my init() method my first NSLog self.currentRound?.gameStats is nil when in the function loadData() it wasn't ? It seems like it's losing its reference since we are leaving the function.
What am I doing right now is adding a currentStats property in my singleton, then when I retrieve data instead of doing self.currentRound.gameStats = stats I do self.currentStats = stats, then self.currentRoud.gameStats = self.currentStats and If I do that it works, I don't really know If I am doing the things right here.
Also my two objects Round and GameStats conform to NSCoding protocol as I implemented #objc func encodeWithCoder and #objc required init?(coder aDecoder: NSCoder) methods for both of them.
Thank for you help.

Resources