Segues in Swift - xcode

im new to swift / xcode. I am trying to set up a facebook login.
The login works fine but when the login is successful the segue doesnt appear to work. There are no errors it just doesnt go to the next viewcontroller.
I suspect the issue lies here somehow:
self.performSegueWithIdentifier("showNew", sender: self)
any ideas?
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
class ViewController: UIViewController, FBSDKLoginButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if (FBSDKAccessToken.currentAccessToken()==nil){
println("not Logged in")
}
else{
println("Logged in!")
self.performSegueWithIdentifier("showNew", sender: self)
}
var loginButton = FBSDKLoginButton()
loginButton.readPermissions = ["public_profile","email","user_friends"]
loginButton.center = self.view.center
loginButton.delegate = self
self.view.addSubview(loginButton)
// 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.
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if error == nil {
println("Login Complete")
}
else
{
println(error.localizedDescription)
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
println("User Logged Out!")
}
}

Your forgot to declare the identifier.

I assume that you want the app to move to next view controller after the FB login. Basically move on login success.
Move
self.performSegueWithIdentifier("showNew", sender: self)
to loginButton delegate.
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!)
{
if (error == nil)
{
println("Login Complete")
self.performSegueWithIdentifier("showNew", sender: self)
}
else
{
println(error.localizedDescription)
}
}
If your segue was not defined properly, the app would have crashed. Since it is not crashing at self.performSegueWithIdentifier, you can believe that it is defined.

Related

Swift - Failed (found nil) calling reloadData() from another class but succeeded from self class

I'm apparently designing a drag and drop dropbox which can either select files by clicking it or dragging and dropping the files on it, and I want the selected files to be visible in a table next to it. My design logic is that whenever the user selects files from an NSOpenPanel, it passes the selected file paths into the CoreData and then an array retrieves them one by one from the CoreData, and finally, update the NSTableView's content by using reloadData().
Basically, my problem is that whenever I try to call ViewController().getDroppedFiles() from DropboxButton class, I always get a Fatal error: unexpectedly found nil while unwrapping an optional value.
My ViewController.swift:
import Cocoa
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
getDroppedFiles()
}
#IBOutlet weak var DroppedFilesTableView: NSTableView!
var droppedFiles: [DroppedFiles] = [] // Core Data class definition: DroppedFiles
func numberOfRows(in tableView: NSTableView) -> Int {
return droppedFiles.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let droppedFilesCollection = droppedFiles[row]
if (tableView?.identifier)!.rawValue == "fileNameColumn" {
if let fileNameCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileNameCell")) as? NSTableCellView {
fileNameCell.textField?.stringValue = droppedFilesCollection.fileName!
return fileNameCell
}
} else if (tableView?.identifier)!.rawValue == "filePathColumn" {
if let filePathCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "filePathCell")) as? NSTableCellView {
filePathCell.textField?.stringValue = droppedFilesCollection.filePath!
return filePathCell
}
}
return nil
}
#IBAction func DropboxClicked(_ sender: NSButton) {
// selected file paths
for filePath in selectedFilePaths {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
let droppedFilesData = DroppedFiles(context: context)
droppedFilesData.fileName = getFileName(withPath: filePath)
droppedFilesData.filePath = filePath
do {
try context.save()
} catch {
print("Unable to save core data.")
}
}
getDroppedFiles()
}
}
func getDroppedFiles() {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
do {
try droppedFiles = context.fetch(DroppedFiles.fetchRequest())
} catch {
print("Unable to fetch core data.")
}
}
DroppedFilesTableView.reloadData() // Fatal Error: unexpectedly found nil while unwrapping an optional value (whenever I call this function in other class)
}
}
I'm using a push button (NSButton) as the dropbox (it has its own class), which can easily be clicked and also supports dragging options.
My DropboxButton.swift:
import Cocoa
class DropboxButton: NSButton {
required init?(coder: NSCoder) {
super.init(coder: coder)
registerForDraggedTypes([NSPasteboard.PasteboardType.URL, NSPasteboard.PasteboardType.fileURL])
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
// some other codes
return .copy
}
override func draggingExited(_ sender: NSDraggingInfo?) {
// some other codes
}
override func draggingEnded(_ sender: NSDraggingInfo) {
// some other codes
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let pasteboard = sender.draggingPasteboard.propertyList(forType: NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")) as? NSArray,
let filePaths = pasteboard as? [String] else {
return false
}
for filePath in filePaths {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
let droppedFilesData = DroppedFiles(context: context)
droppedFilesData.fileName = getFileName(withPath: filePath)
droppedFilesData.filePath = filePath
do {
try context.save()
} catch {
print("Unable to save core data.")
}
}
ViewController().getDroppedFiles() // found nil with reloadData() in ViewController.swift
}
return true
}
}
And this is my interface and code logic:
So, how can I reloadData() for the table view in my ViewController class from another class (DropboxButton: NSButton) so that whenever the user drags and drops files into the dropbox, the table view will reload?
P.S. To get this done means a lot to me, I really need to get this fixed in a short time, is there anyone can spend some time and help me?
You need to call getDroppedFiles() on a loaded instance of ViewController.
With ViewController().getDroppedFiles() you're creating a new instance of ViewController that is not shown anywhere (so controls are not initialized resulting in the nil error).
I found this solution useful for my case.
I used observer to pass through data and call functions from other controller classes, now I understand that I was creating a new instance of ViewController which is not loaded. Here is my code:
ViewController.swift:
class ViewController: NSViewController {
// other codes
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(getDroppedFiles), name: NSNotification.Name(rawValue: "reloadTableViewData"), object: nil)
}
#objc func getDroppedFiles() {
DroppedFilesTableView.reloadData()
}
}
DropboxButton.swift:
class DropboxButton: NSButton {
// other codes
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
// other codes
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTableViewData"), object: nil)
return true
}
}
And now, everything works perfectly, I can even add an userInfo: to pass data between files and classes.

Obtaining a View Controller Reference

I read quite a few questions and answers no this problem. Some are for Ojective C. Some are for iOS. The ones that were close to what I need didn't work.
I've set up a protocol for delegation. It doesn't work. The problem is that delegate variable isn't set. I need the reference to an active controller.
Delegator
protocol SwitchTabDelegate: class {
func selectTab(tab: Int)
}
class ViewController: NSViewController {
weak var delegate: SwitchTabDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func selectCompositions(_ sender: NSButton) {
if let delegate = self.delegate {
delegate.selectTab(tab: 2)
}
else {
print("self.delegate is nil")
}
print("delegate called")
}
}
Delegatee
class TabViewController: NSTabViewController, SwitchTabDelegate {
var viewController : ViewController?;
override func viewDidLoad() {
super.viewDidLoad()
//viewController = storyboard?.instantiateController(withIdentifier: "viewController") as? ViewController
// viewController?.delegate = self
// print(viewController)
}
func selectTab(tab: Int) {
print("In the delegate")
switchToDataTab()
}
func switchToDataTab() {
Timer.scheduledTimer(timeInterval: 0.2, target: self,
selector: #selector(switchToDataTabCont),
userInfo: nil, repeats: false)
}
func switchToDataTabCont(){
self.selectedTabViewItemIndex = 2
}
}
The delegatee is the main NSViewContoller. On the storyboard, it contains two buttons and a Container view controller. Embedded in the container view controller is the TabViewController, the delegatee. You can see in the delegatee where I tried to get a reference. It does get a reference, presumably to the newly instantiated instance. I need a reference to the original view controller that was spun up when the application started.
Answer
I added the following code to the delegator:
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let controller = segue.destinationController as! TabViewController
self.delegate = controller as SwitchTabDelegate
}
That's not how it should work following the design pattern. The delegator should have no knowledge of the delegatee. I've spent way too much time on this issue so a hack is going to do.
When using storyboards, you want to "push" references to children when they are created vs. pulling them from an upstream controller. This is what -prepareForSegue:sender: is used for.

GameCenter not working, authentication and leaderboard not showing

With Swift 2, GameCenter is not working for me. The authentication ViewController is not showing up... Here is my func authenticateLocalPlayer():
func authenticateLocalPlayer() {
var localPlayer = GKLocalPlayer()
localPlayer.authenticateHandler = {(viewController: UIViewController?, error: NSError?) -> Void in
if (viewController != nil) {
self.presentViewController(viewController!, animated: true, completion: nil)
print("Not Authenticated. ")
} else {
print("Authenticated. ")
}
}
}
It is returning "Not Authenticated" every time, but is not presenting the ViewController. Any solution?
This solution presents the viewController correctly using Swift 2 in Xcode 7.0.
Note that I have changed the code before the if statement begins. I believe the syntax may have changed in a recent software update as I had this problem too.
In my app I called authenticateLocalPlayer() in the viewDidLoad() method of the GameViewController class.
func authenticateLocalPlayer() {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}

Delegate protocol not working, returns nil

I'm trying to use a protocol / delegate in swift, and while I'm not getting any errors it seems that my delegate is not being created. It is returning nil on me, and I'm not sure why.
Here is my code
Class 1
import UIKit
protocol GameViewSliding{
func slideGameView()
}
class GameDetailsViewController: UIViewController {
var delegate:GameViewSliding?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func showOptions(sender: AnyObject) {
println("button pressed")
println(delegate)
delegate?.slideGameView()
}
}
Class 2 that conforms to the protocol
import UIKit
var currentHoleNumber:Int = 0
var parThree = false;
var parFive = false;
class GameViewController: UIViewController, GameViewSliding{
var gameDetailsVC:GameDetailsViewController = GameDetailsViewController()
override func viewDidLoad() {
super.viewDidLoad()
println("inside the game class")
gameDetailsVC.delegate = self
}
func slideGameView(){
println("this is from the root controller")
}
}
The problem is likely in the code you are not showing here. In prepareForSegue you usually want to set the delegate on the destination view controller.
Essentially you are setting it on an instance of the class that you are creating, but that isn't the instance that is actually shown. So the instance that is shown has no delegate.
Remove your local var of the second controller, and the setting of the delegate in the view did load and simply set it on the destination in prepare for segue and I bet it will work perfectly.

HMHomeManager didAddHome not returned

I am having a problem with adding homes with HMHomeManager. I can call the function add well, but HMHomeManager does not return
func homeManager(manager: HMHomeManager!, didAddHome home: HMHome!) {
println("\(__FUNCTION__)")
}
I am sure I already assigned the delegate of homeManager. Below is my code:
class ViewController: UIViewController, HMHomeManagerDelegate {
var manager: HMHomeManager
required init(coder aDecoder: NSCoder) {
manager = HMHomeManager()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
manager.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Actions
#IBAction func home1Pressed(sender: UIButton) {
manager.addHomeWithName("Home12", completionHandler: {
(home:HMHome!, error:NSError!) -> Void in
if error != nil {
println("error: \(error)")
}
else {
println("no error")
}
})
}
#IBAction func home2Pressed(sender: UIButton) {
manager.addHomeWithName("Home23", completionHandler: {
(home:HMHome!, error:NSError!) -> Void in
if error != nil {
println("error: \(error)")
}
else {
println("no error")
}
})
}
// MARK: - HMHomeManagerDelegate
func homeManagerDidUpdateHomes(manager: HMHomeManager!) {
println("\(__FUNCTION__)")
}
func homeManagerDidUpdatePrimaryHome(manager: HMHomeManager!) {
println("\(__FUNCTION__)")
}
func homeManager(manager: HMHomeManager!, didAddHome home: HMHome!) {
println("\(__FUNCTION__)")
}
func homeManager(manager: HMHomeManager!, didRemoveHome home: HMHome!) {
println("\(__FUNCTION__)")
}
}
Note that: when I first run this code, homeManagerDidUpdateHomes is called which proves to me that homeManager.delegate=self is correct
In this code, after I pressed button1 (home1Pressed triggered) and button2 (home1Pressed triggered), home12 and home23 are created. I know this because next time I run this code, the output is:
homeManagerDidUpdateHomes
homes: [[ name = Home1, primary : Yes ], [ name = Home12, primary : No ], [ name = Home23, primary : No ]]
But Why homeManager(manager: HMHomeManager!, didAddHome home: HMHome! is not called when I pressed button1 and button2? I cannot figure it out, for 2 days.
Thanks,
Did you remember to enable HomeKit in Capabilities?
Quoting from Apple Dev Forum discussions:
This is not a bug but yeah, it should be better documented.
All HomeKit's delegates will only get invoked when there are changes in HomeKit database that are not caused by the app or doesn't have a direct connection to your completion handler (Like Synced from iCloud, another HomeKit app added a room then user switched to your app)
I get the same error. As a workaround, I am getting the homeManagerDidUpdatePrimaryHome callback. I filed a bug for this during the beta, which was closed as a duplicate, but I guess it needs to be refiled.
homeManagerDidUpdatePrimaryHome is working, but that really only helps with the first home.
I re-reported this to Apple.
This is an iOS bug (tested with 8.3).
21097472 HomeKit API - HMHomeManagerDelegate didAddHome is not called

Resources