Does Core Data Use Threading By Default? - ios

I was thinking of using coreData to store my username and password for persistence from the users. While testing I noticed that when I quickly close the program after it saves the data and relaunch it to check if the data was persisted that it would sometimes say nothing was there, and then when I relaunch the app again then it would be there. The longer I waited the more likely when I relaunched the app that the persisted data would appear.
I was adding coreData to an existing project so I created a controller called DataController.swift and copied the suggested code from apple. That code is below.
import UIKit
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
override init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("AppSettings", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
self.managedObjectContext.persistentStoreCoordinator = psc
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("AppSettings.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
}
My LoginViewController.swift is below:
import UIKit
import CoreData
class LoginViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var passwordField: UITextField!
let moc = DataController().managedObjectContext
#IBAction func SignUpButtonPressed(sender: UIButton) {
print("sign up")
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
print("view loaded, check if already signed in here")
let loggedIn = checkIfLoggedInAlready() //checks database to see
if(loggedIn){
print("was logged in!")
}
}
func checkIfLoggedInAlready() -> Bool{
let fetchRequest = NSFetchRequest(entityName: "AppSettings")
//let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) //Deletes ALL appsettings entities
do {
let fetchRequest = try moc.executeFetchRequest(fetchRequest) as! [AppSettings]
guard let appSettingsArrayItem = fetchRequest.first where fetchRequest.count>0 else {
print ("no entities found...")
return false
}
guard let username = (appSettingsArrayItem as AppSettings).username else{
print ("username not found")
return false
}
print("number Of AppSetting Entities =\(fetchRequest.count)")
print(username)
//The following code deletes ALL the entities!
//try moc.persistentStoreCoordinator!.executeRequest(deleteRequest, withContext: moc)
//To delete just '1' entry use the code below.
//moc.deleteObject(appSettingsArrayItem)
// try moc.save()//save deletion change.
print("deleted particular entity item")
return true
} catch{
fatalError("bad things happened \(error)")
}
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
print("prepar seque")
}
func displayErrorMessage(errorMessage: String){
print("show error console with Error:"+errorMessage)
let alert = UIAlertController(title: "Error", message: errorMessage, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
switch(identifier){
case "loginSegue":
print("loginSegue Check")
guard let password = passwordField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !password.isEmpty else {
displayErrorMessage("Password can not be empty!")
return false
}
guard let username = usernameField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !username.isEmpty else{
displayErrorMessage("Username can not be empty!")
return false
}
let url = "http://mywebsite.com/restapi/v1/userlogin?email="+username+"&password="+password
print(url)
let json = JSON(url:url)
print(json)
if(json["status"].asInt==1){
let entity = NSEntityDescription.insertNewObjectForEntityForName("AppSettings", inManagedObjectContext: moc) as! AppSettings
entity.setValue(username, forKey: "username")
entity.setValue(password, forKey: "password")
entity.setValue(json["tokenid"].asString, forKey: "token")
entity.setValue(json["roleid"].asInt, forKey: "roleid")
entity.setValue(json["role"].asString, forKey: "role")
entity.setValue(json["companyid"].asInt , forKey: "companyid")
entity.setValue(json["isdev"].asInt, forKey: "isdev")
//save token and other details to database.
do {
try moc.save()
print("saved to entity")
}catch{
fatalError("Failure to save context: \(error)")
}
//token
//roleid int
//role
//companyid int
return true //login succesfull
}else{
displayErrorMessage("Incorrect Username or Email")
return false//failed
}
default:
displayErrorMessage("Unknown Error Related To Segue Not Found")
}
return false //if it gets to this point assume false
}
}
To test my code you would just uncomment the part that deletes the entity (//moc.deleteObject(appSettingsArrayItem)
) in the function checkIfLoggedInAlready().
In short though the answer to this question is a simple yes or no, I have a hunch its threaded and that is why the delay matters. I think its threaded because of this line in the DataController.swift
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0))
but not sure if that is what is affecting it. The code that creates the entity is done in shouldPerformSegueWithIdentifier of LoginViewController.swift

Not by default. All of the NSManagedObjects are bound to NSManagedContext which has it's own queue. If you want concurrency, you need to create another NSManagedContext with private queue. You can read about concurrency in Core Data over here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Also, never use dispatch_async for Core Data. Each context has it's own peformBlock which run on it's queue.

Related

Duplicate values after saving to CoreData

I currently have 2 views in my project: a ViewController with a TableView that shows a list of Transactions and a ViewController for creating a new Transaction.
The issue that I am having is that after creating a new Transaction, it successfully loads onto my TableView, but when I force close and reopen the app, the value is duplicated. As far as I can tell and know, the save() function is only being called once and nothing of the sort is going on in AppDelegate/SceneDelegate. See GIF for an example of what's going on.
GIF Demonstrating Issue
This is my TranasctionsTableViewController:
class TransactionsTableViewController: UIViewController {
// MARK: - Properties
var entries: [NSManagedObject] = []
var container: NSPersistentContainer!
// MARK: - IBOutlets
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// setup persistent container
container = NSPersistentContainer(name: "expenses")
// load store
container.loadPersistentStores { storeDescription, error in
if let error = error {
print("Unresolved error \(error)")
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier! {
case "NewTransactionSegue":
// pass an entry to the destination segue
let destination = segue.destination as! UINavigationController
let targetController = destination.topViewController as! NewTransactionViewController
let managedContext = container.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Entry", in: managedContext)!
let entry = NSManagedObject(entity: entity, insertInto: managedContext)
targetController.entry = entry as? Entry
default:
print("Unknown segue: \(segue.identifier!)")
}
}
#IBAction func unwindToTransactionList(sender: UIStoryboardSegue) {
print("unwind segue called!")
if let sourceViewController = sender.source as? NewTransactionViewController, let entry = sourceViewController.entry {
print(entry)
self.save(entryDescription: entry.entryDescription, amount: entry.amount, date: entry.date, id: entry.id)
// reset the tableView to defaults if no data message was displayed before loading data.
if self.tableView.backgroundView != nil {
self.tableView.backgroundView = nil
self.tableView.separatorStyle = .singleLine
}
self.tableView.reloadData()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// load managedCOntext
let managedContext = container.viewContext
// try to fetch data from CoreData. If successful, load into entries.
do {
entries = try managedContext.fetch(Entry.fetchRequest())
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
// save a new entry to the data store
func save(entryDescription: String, amount: Double, date: Date, id: UUID) {
print("save called!!")
let managedContext = container.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Entry", in: managedContext)!
let entry = NSManagedObject(entity: entity, insertInto: managedContext)
entry.setValue(entryDescription, forKey: "entryDescription")
entry.setValue(amount, forKey: "amount")
entry.setValue(date, forKey: "date")
entry.setValue(id, forKey: "id")
do {
try managedContext.save()
entries.append(entry)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
// save the current container context
func saveContext() {
if container.viewContext.hasChanges {
do {
try container.viewContext.save()
} catch {
print("An error occurred while saving: \(error)")
}
}
}
}
This is my NewTransactionViewController:
class NewTransactionViewController: UIViewController, UITextFieldDelegate {
/*
This value is either passed by `TransactionTableViewController` in `prepare(for:sender:)`
or constructed as part of adding a new transaction.
*/
var entry: Entry?
// MARK: - IBOutlets
#IBOutlet weak var transactionDescriptionLabel: UILabel!
#IBOutlet var transactionDescriptionTextField: UITextField!
#IBOutlet var transactionAmountLabel: UILabel!
#IBOutlet var transactionAmountTextField: UITextField!
#IBOutlet weak var cancelButton: UIBarButtonItem!
#IBOutlet weak var saveButton: UIBarButtonItem!
// MARK: Constants
let TRANSACTION_DESCRIPTION_TEXT_FIELD_TAG = 0
let TRANSACTION_AMOUNT_TEXT_FIELD_TAG = 1
override func viewDidLoad() {
super.viewDidLoad()
// Handle textfield input through delegate callbacks.
transactionDescriptionTextField.delegate = self
transactionAmountTextField.delegate = self
// Adds done button to keypad
transactionAmountTextField.addDoneButtonToKeyboard(myAction: #selector(self.transactionAmountTextField.resignFirstResponder))
}
// MARK: - UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// If the First Responder is the Description Text Field, transfer First Responder to Amount Text Field
if (textField.tag == TRANSACTION_DESCRIPTION_TEXT_FIELD_TAG) {
transactionAmountTextField.becomeFirstResponder()
}
textField.resignFirstResponder()
return true
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: segue)
// Configure the destination view controller only when the save button is pressed.
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("The save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let transactionDescription = transactionDescriptionTextField.text ?? ""
let transactionAmount = Double(transactionAmountTextField.text!) ?? 0.00
// Set the entry to be passed to TransactionTableViewController after the unwind segue.
entry?.setValue(transactionDescription, forKey: "entryDescription")
entry?.setValue(transactionAmount, forKey: "amount")
entry?.setValue(UUID(), forKey: "id")
entry?.setValue(Date(), forKey: "date")
}
// MARK: - IBActions
#IBAction func cancelButtonTapped(sender: UIBarButtonItem) {
}
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
}
}

IOS. enter data into a table (name and price of pizza) and view it on another view controller (in label)

Have set up a class for the core data code to save and retrieve, then on the first view controller have a form to enter the price and name of the pizza, a save button to have the save method linked from the dbmanager, and a view button for viewing the saved data in the table.
now im not sure if my save method is correct but I have no errors for it yet.
But i cant quite work out how to show the entered data in the table of the second view controllers label.
this is my first attempt on IOS
the first view controller:
import UIKit
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var price: UITextField!
#IBOutlet weak var name: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func save(_ sender: Any) {
let db = DatabaseManager()
db.addRow(name: name.text!, price: price.text!)
}
#IBAction func view(_ sender: Any) {
let db = DatabaseManager()
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.identifier == "mySegue" {
let controller : allPizzas = segue.destination as! allPizzas
this line here below says cannot assign value of type [string] to string
controller.pizzaTxt = db.retrieveRows()
}
}
`enter code here`// }
func showMessage(msg: String)
{
let alert = UIAlertController(title: "Message", message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
the second view controller:
import UIKit
import CoreData
class allPizzas: UIViewController {
//label to display the pizzas
#IBOutlet weak var output: UILabel!
var pizzaTxt : String = ""
override func viewDidLoad() {
super.viewDidLoad()
output.text = pizzaTxt;
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
the database manager:
import UIKit
import CoreData
class DatabaseManager: NSObject {
var pizzas: [NSManagedObject] = []
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func addRow( name:String, price:String) {
// set the core data to access the Student Entity
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Pizzas", in: managedContext)
let pizza = NSManagedObject(entity: entity!, insertInto: managedContext)
pizza.setValue(name, forKey: "name")
pizza.setValue(price, forKey: "price")
do {
try managedContext.save()
pizzas.append(pizza)
//showMessage(msg: "Information is added")
}
catch {
//showMessage(msg: "Error While Adding to Core Data")
}
}
func retrieveRows() -> [String] { // return array of Strings
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return [""]}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Pizzas")
do {
pizzas = try managedContext.fetch(fetchRequest)
}
catch{
// show something
}
var msg : [String] = []
var count = 0
for pizza in pizzas {
msg.append((pizza.value(forKeyPath: "name") as? String)!)
count += 1
}
msg = [String(count)]
return msg
}
}

My code for NSUserDefaults is not working

I am very new to swift. And need your help!
I want that, when the user logs in for the second time , the app should directly take it to the next view controller named CoreView. It should not ask for details, but I don't know why its not working. And it's asking for details everytime the app is launched. Please check the below code. I am not getting any sort of error too. Unless and until the app is killed or logged out, the user should be able to log in directly .
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
And in the viewDidLoad function:
override func viewDidLoad()
{
super.viewDidLoad()
//Read the data
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
pref_write()
let prefs = NSUserDefaults.standardUserDefaults()
check_OTP = prefs.objectForKey("OTP")!
check_U_ID = prefs.objectForKey("U_ID")!
prefs.objectForKey("U_ID")
print("prefs:\(prefs)")
prefs.synchronize()
}
Thanks!
Create a class as
class User_Details : NSObject
{
var user_id : String?
var user_otp : String?
var otp_verified : Bool?
init(u_id:String, otp:String?, verified:Bool)
{
super.init()
self.user_id = u_id
self.otp_verified = verified
self.user_otp = otp
}
}
In AppDelegate,
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
navController = self.window?.rootViewController as? UINavigationController
if self.checkIfUserLoggedIn()
{
let user_details = NSUserDefaults.standardUserDefaults().objectForKey("user_details") as! User_Details
self.moveToNextScreen(user_details)
}
return true
}
//AppDelegate Class or in the class which is globally accessible
func pref_write_user(user_details : User_Details)
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(user_details, forKey: "user_details")
prefs.setBool(true, forKey: "is_user_login")
//After saving the OTP for current user, check for otp verified, move to OTP Screen
self.moveToNextScreen(user_details)
}
func moveToNextScreen(user_details : User_Details)
{
if user_details.otp_verified == false
{
// Move to OTP screen
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("otpScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
else // Move to Home Screen
{
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("homeScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
}
func logoutUser()
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(nil, forKey: "user_details")
prefs.setBool(false, forKey: "is_user_login")
}
func checkIfUserLoggedIn() -> Bool
{
let prefs = NSUserDefaults.standardUserDefaults()
if prefs.boolForKey("is_user_login")
{
if let _ = prefs.objectForKey("user_details")
{
return true
}
else
{
//User details not found for some reason, so setting the inital values and return false
self.logoutUser()
}
}
return false
}
Login Class :
Call the API for login by providing the basic credential, get the user_id and user_otp, save them to NSUserDefaults
func requestLoginToServer()
{
//Perform basic server action
....
//In Success Block write this
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
// pass the values as return by the server
let user_details = User_Details(u_id: "123", otp: "1234", verified: false)
appDel.pref_write_user(user_details)
appDel.moveToNextScreen(user_details)
}
Please try this way. I just rearranged your code.
First it will check the login credentials with in the didload method of initial view controller. If it not there it will call the method pref_write() . Please make sure that the values used in pref_write() method are not nil
override func viewDidLoad()
{
super.viewDidLoad()
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
// You can give conditions for your need like if(prefs.valueForKey("U_ID") != nil))
// It will check the user defaults whether you already login
if(prefs.valueForKey("OTP") != nil) {
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
else{
pref_write()
}
}
// Make sure the Values are not nil
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
Hope its working...

Unexpected quirky behavior from socket.io in Swift

As per title, I'm having some trouble dealing with socket.io. It connects really well and accordingly in the first view controller but weird things happen when it comes to second controller.
Here's the code:
First Controller: I have declared some global variable for connection purposes between both view controller.
import UIKit
import SocketIOClientSwift
import SwiftyJSON
import CoreData
//declare some global variable
var patientCoreData = [NSManagedObject]()
var numberOfUsersExisting:Int = 0 //assign to 0 by default
var appUserData: Patient? //for specific user
var pSample: Array<Patient> = [] //for all user
//initiate socket globally
let socket = SocketIOClient(socketURL: "localhost:3000", options: [
"reconnects": true
])
func reportStatus(){
socket.on("connect") {data, ack in
print("Report status: View Controller connected")
socket.emit("click", "Client app connected")
}
}
func readDataFromSocket(completion: (data:AnyObject)-> ()){
socket.on("reply") {data, ack in
print("database replied")
completion(data: data)
}//socket
}//readDataFromSOCKET
func importData(){
reportStatus()
socket.connect()
readDataFromSocket(){ data in
let json = JSON(data)
let nou = json[0].count
if nou > 0 {
print("Test(1st VC): grabbing data from database")
for var i=0; i<nou; ++i{
numberOfUsersExisting = nou
pSample += [Patient(id: json[0][i]["ID"].intValue, name: json[0][i]["Name"].stringValue, gender: json[0][i]["Gender"].stringValue, mileage: json[0][i]["Mileage"].doubleValue)]
pSample.sortInPlace({$0.globalPatientMileAge < $1.globalPatientMileAge})
}
print("Successfully grabbed data")
}else{
print("No user in the database")
numberOfUsersExisting = 0
}
}//readDataFromSocket
}
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("First view appeared")
let prefs = NSUserDefaults.standardUserDefaults()
//if an user has logged in
let isLoggedIn = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1){
print("No user currently, so heading to login screen")
socket.disconnect()
self.performSegueWithIdentifier("gotoLogin", sender: self)
}else{
print("ViewDidAppear: An user has been logged in")
let permissionToLoadData = prefs.integerForKey("ISLOGGEDIN")
if (permissionToLoadData != 1) {
print("Please grant permission to get data")
}else{
print("First view: connecting to database")
importData()
}//permission to load data
}
}//end of viewDidAppear
}
Second Controller:
import UIKit
import SocketIOClientSwift
import SwiftyJSON
import CoreData
var nou:Int?
class LoginViewController: UIViewController {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let registeredUserID = NSUserDefaults.standardUserDefaults().stringForKey("registerPatientID")
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
func displayAlertMessage(userMessage:String){
let alert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
alert.addAction(okAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func successMessage(userMessage:String){
let alert = UIAlertController(title: "Welcome Back", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
alert.addAction(okAction)
self.presentViewController(alert, animated: true, completion: nil)
}
#IBOutlet weak var loginPatientID: UITextField!
#IBAction func LoginButton(sender: AnyObject) {
let logInUserID = loginPatientID.text
if (logInUserID!.isEmpty){
displayAlertMessage("Please enter your Patient ID!")
return
}else{
print("Test: requesting login permission from database")
socket.emit("loginRequest", logInUserID!)
print("Test: requested")
socket.on("loginReply") {data, ack in
let jsonLogin = JSON(data)
if jsonLogin[0].intValue == 1{
print("Test: ID Matched, putting up ViewController")
self.prefs.setObject(logInUserID, forKey: "AppUserID")
self.prefs.setInteger(1, forKey: "ISLOGGEDIN")
self.prefs.synchronize()
let permissionToLoadData = self.prefs.integerForKey("ISLOGGEDIN")
if (permissionToLoadData != 1) {
print("Please grant permission to get data")
}else{
print("First view: connecting to database")
importData()
print("Did you import?")
}//permission to load data
self.loginPatientID.resignFirstResponder()
self.dismissViewControllerAnimated(true, completion: nil)
}else if jsonLogin[0].intValue == 0{
self.displayAlertMessage("Sorry, you are not assigned to this program")
}else if jsonLogin[0].intValue == 3{
print("Test: Query problem")
}else{
print("Test: not getting anything from ID database")
}
}//socket.on
}//else
}//login button
override func viewDidLoad() {
super.viewDidLoad()
print("Login View Controller loaded")
}
override func viewDidAppear(animated: Bool) {
socket.connect()
print("LoginVC: establishing connection")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You may have noticed that in First view controller, when the viewDidAppear() is launched, the app will checks if user is login or not. If somebody has already logged in, it's fine. If there is nobody logging in, it will perform a segue(modally segue) to Second view controller.
A login form will be presented in second view controller and once user hits the login button, you might wanna look at the code.
Let's assume that everything goes right until it comes to importData(), the function isn't launched at all but the app just goes on, why?
Here's a screenshot of the console, pay attention to "Did you import?", if the function is launched, the app should return some additional message from 1st view controller.
After struggling for a few days, I think I may have found the correct answer.
Eventually I defined 2 different socket handlers connection as such:
let loginSocket = SocketIOClient(socketURL: "localhost:3000")
let socket = SocketIOClient(socketURL: "localhost:3000", options: [
"reconnects": true
])
for both view controller.
If there is a conclusion I can draw from this conundrum is that we can't use single socket handler for socket methods from different view controller.

swift prepareforsegue with mmdrawercontroller

I have searched every where to find a solution to this error i am getting, i have also tried to use, nsuserdefaults, struct and global var to pass on my vars to other viewcontrollers. I am use mmdrawer and i have set a navigationcontrol on my first viewcontroller that who i named userOview and identifier membersArea. Whenever i try to use prepareforsegue i am getting the following error
Could not cast value of type 'xxxxxxxx.DjInformation' (0x115da8) to 'UINavigationController' (0x3ad405e0).
My appdelegate looks like this
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var centerContainer: MMDrawerController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn == 1){
var rootViewController = self.window!.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var centerViewController = mainStoryboard.instantiateViewControllerWithIdentifier("memberArea") as! userOverview
var leftViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LeftSideViewController") as! LeftSideViewController
var rightViewController = mainStoryboard.instantiateViewControllerWithIdentifier("RightSideViewController")as! RightSideViewController
var leftSideNav = UINavigationController(rootViewController: leftViewController)
var centerNav = UINavigationController(rootViewController: centerViewController)
var rightNav = UINavigationController(rootViewController: rightViewController)
centerContainer = MMDrawerController(centerViewController: centerNav, leftDrawerViewController: leftSideNav,rightDrawerViewController:rightNav)
centerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.PanningCenterView;
centerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.PanningCenterView;
window!.rootViewController = centerContainer
window!.makeKeyAndVisible()
UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: UIStatusBarAnimation.None)
}
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "bfd.Be_Fit_Donate" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("Be_Fit_Donate", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("Be_Fit_Donate.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
coordinator = nil
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
}
and the view controller i am trying to send the prepareforsegue from is as follows
import UIKit
import AVFoundation
import AVKit
import Foundation
import Social
public var AudioPlayer = AVPlayer()
public var SelectedSongNumber = Int()
public var TrackName = String()
public var TrackImage = String()
public var TrackDJ = String()
class MusicListTableViewController: UITableViewController, AVAudioPlayerDelegate{
#IBOutlet weak var playerView: UIView!
#IBOutlet weak var songName: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var trackDjName: UILabel!
#IBOutlet weak var imageArtwork: UIImageView!
var trackName = [String]()
var artistLabel = [String]()
var trackUrl = [String]()
var artWork = [String]()
var tags = [String]()
var artistId = [String]()
var djInfo = ""
#IBAction func facebookButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
var facebookSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookSheet.setInitialText("Share on Facebook")
self.presentViewController(facebookSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func tritterButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
var twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twitterSheet.setInitialText("Share on Twitter")
self.presentViewController(twitterSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func myPlayList(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("myMusicList") as! myMusicList
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
#IBAction func favoriteButton(sender: UIButton) {
var favSong = trackName[SelectedSongNumber]
var alertView:UIAlertView = UIAlertView()
alertView.title = "Nummer Toegevoegd"
alertView.message = "Het nummer \(favSong) is nu toegevoegd aan uw favorieten muziek lijst"
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
#IBAction func djInformation(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var artist_label = artistLabel[SelectedSongNumber]
if (segue.identifier == "djInformation" ){
var detailVC = segue.destinationViewController as! UINavigationController
let targetController = detailVC.topViewController as! DjInformation
targetController.djInfo = "hello"
}
}
func getMusicListJSON(){
let urlString = "http://xxxxxxxxxx"
let urlEncodedString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let url = NSURL( string: urlEncodedString!)
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, innerError) in
let json = JSON(data: data)
let musicArray = json.arrayValue
//NSLog("\(json)")
dispatch_async(dispatch_get_main_queue(), {
for musiclist in musicArray
{
let track_name = musiclist["track_name"].stringValue
let artist = musiclist["artist"].stringValue
let track_url = musiclist["track_url"].stringValue
let art_work = musiclist["artwork"].stringValue
let track_tags = musiclist["tags"].stringValue
let artist_id = musiclist["artist_id"].stringValue
//println( "track_name: \(track_name) artist: \(artist) track_url: \(track_url) artwork: \(art_work) track_tags: \(track_tags) artist_id: \(artist_id)" )
self.trackName.append(track_name)
self.artistLabel.append(artist)
self.trackUrl.append(track_url)
self.artWork.append(art_work)
self.tags.append(track_tags)
self.artistId.append(artist_id)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
})
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
getMusicListJSON()
playButton.addTarget(self, action: "playButtonTapped:", forControlEvents: .TouchUpInside)
playerView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height * 0.7)
playButton.hidden = true
var error: NSError?
var success = AVAudioSession.sharedInstance().setCategory(
AVAudioSessionCategoryPlayAndRecord,withOptions: .DefaultToSpeaker, error: &error)
if !success{
NSLog("Failed to set audio session category, Error: \(error)")
}
}
func playButtonTapped(sender: AnyObject){
// set play image to pause wehen video is paused and also back
if AudioPlayer.rate == 0
{
AudioPlayer.play()
playButton.setImage(UIImage(named:"pause"), forState: UIControlState.Normal)
} else {
AudioPlayer.pause()
playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
}
}
#IBAction func slideOutMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
#IBAction func musicMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = true
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return trackName.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("playerCell", forIndexPath: indexPath) as! playerTableViewCell
// Configure the cell...
cell.artistName.text = trackName[indexPath.row]
cell.trackName.text = artistLabel[indexPath.row]
return cell
}
// set the song to play according to the table row selected. Also set the name and artist.
func playSong() {
var playnumber = trackUrl[SelectedSongNumber]
var TrackName = trackName[SelectedSongNumber]
var TrackImage = artWork[SelectedSongNumber]
var TrackDJ = artistLabel[SelectedSongNumber]
var DjId = artistId[SelectedSongNumber]
AudioPlayer = AVPlayer(URL: NSURL(string: playnumber))
AudioPlayer.play()
songName.text = TrackName
trackDjName.text = TrackDJ
load_artwork(TrackImage)
playButton.hidden = false
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
SelectedSongNumber = indexPath.row
playSong()
}
// set the song artwork to display in the player for the current playing song.
func load_artwork(urlString: String){
var imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
if error == nil {
self.imageArtwork.image = UIImage(data: data)
}
})
}
}
These are the two codes that i think should be the cause of the problem. But so far i can't find any way to pas any variables and i will be needing that to complete my app. What am i doing wrong here?
I can really use some help here to get this to work. Maybe my navigation controller is correctly setup. As the first viewcontroller should be embedded in it. But i am not sure i have done that properly.
Thanks for the help.
I have found the solution which was very simple if you know what you should do. When you are using mmdrawer and would like to send over data you could just create a prepareforsegue, but you also need to create a segue with overrides the mmdrawer. I find that you use you use the following just like you would do with prepareforsgue
#IBAction func djInformation(sender: UIButton) {
var artist_label = artistLabel[SelectedSongNumber]
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
centerViewController.djINfo = "\(artist_label)"
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
so basically what you do in prepareforsegue you can also do in button that is initiating the mmdrawercontroller.
Hope this makes sense and helps some one else. I am in no way a pro and i am not sure if this is proper or more something like a hack. but it fast and easy.

Resources