Facebook and Twitter Integration in Xcode Swift iOS Scripting

I have developed a game in Xcode using a set of sprites and scenes. Now I am trying to integrate functionality to post high scores on Twitter and Facebook. I've looked around and most people say to use SLComposeServiceViewController and that's ok until I try to imagine it. Since my application really only uses scenes, they never have a "presentViewController (....)" member function. Thus, I cannot imagine it. Does anyone know about this?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch:UITouch = touches.first!
    let touchLocation = touch.location(in: self)
    let touchedNode = self.atPoint(touchLocation)

    if (touchedNode.name == "tryAgain") {
        let nextScene = Scene_LiveGame(size: self.scene!.size)
        nextScene.scaleMode = self.scaleMode
        self.view?.presentScene(nextScene, transition: SKTransition.fade(withDuration: 0.5))
    }
    else if (touchedNode.name == "share") {

        if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeFacebook) {

        let fShare = SLComposeViewController(forServiceType: SLServiceTypeFacebook)



        self.presentViewController(fShare!, animated: true, completion: nil)
         //^This is where my problem is. Xcode is telling me that self has no member function presentViewController which I totally understand, because its a scene and thus doesn't share those functions. But every resource online has shown me this is the only way to do it   

        }

    }

      

+3


source to share


3 answers


I won't go into the SLComposeViewController

linked code. I'll just show you two techniques besides what crashoverride777 suggested. Therefore, the first method will use notifications, for example:

GameScene:

import SpriteKit

let kNotificationName = "myNotificationName"

class GameScene: SKScene {


    private func postNotification(named name:String){

        NotificationCenter.default.post(
            Notification(name: Notification.Name(rawValue: name),
                         object: self,
                         userInfo: ["key":"value"]))
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


        self.postNotification(named: kNotificationName)

    }
}

      

This is where you post a notification by tapping on the screen. The desired view controller class can listen for this notification, for example:



import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()


        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.handle(notification:)),
            name: NSNotification.Name(rawValue: kNotificationName),
            object: nil)

        if let view = self.view as! SKView? {
            // Load the SKScene from 'GameScene.sks'
            if let scene = GameScene(fileNamed: "GameScene") {
                // Set the scale mode to scale to fit the window
                scene.scaleMode = .aspectFill

                // Present the scene
                view.presentScene(scene)
            }
        }
    }

    func handle(notification:Notification){
        print("Notification : \(notification)")
    }
}

      

Here we are adding self as an observer for this notification - means that on notification, the appropriate processing method will be called (and this is our custom method handle(notification:)

). In this method, you must call your code:

if SLComposeViewController.isAvailable(forServiceType:   SLServiceTypeFacebook) {
     let fShare = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
     self.presentViewController(fShare!, animated: true,  completion: nil)
}

      

Actually, I'll write another example for delegation to keep it clean :)

+1


source


You are getting this error because you need to expose a UIViewController from another UIViewController. So

self.presentViewController(...)

      

won't work because self (SKScene) is not a UIViewController. To represent from SKScene you have to say this

view?.window?.rootViewController?.presentViewController(fShare!, animated: true, completion: nil)

      

I would recommend that you no longer use these APIs. Better to use UIActivityViewController for your sharing needs. This way you only need one shareable button in the app and you can use all kinds of services (email, Twitter, Facebook, iMessage, WhatsApp, etc.).

Create a new Swift file and add this code.

enum ShareMenu {

    static func open(text: String, image: UIImage?, appStoreURL: String?, from viewController: UIViewController?) {
        guard let viewController = viewController, let view = viewController.view else { return }

    // Activity items
    var activityItems = [Any]()

    // Text
    activityItems.append(text)

    // Image
    if let image = image {
        activityItems.append(image)
    }

    /// App url
    if let appStoreURL = appStoreURL {
        let items = ActivityControllerItems(appStoreURL: appStoreURL)
        activityItems.append(items)
    }

    // Activity controller
    let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

    // iPad settings
    if UIDevice.current.userInterfaceIdiom == .pad {
        activityController.modalPresentationStyle = .popover
        activityController.popoverPresentationController?.sourceView = view
        activityController.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
        activityController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0)
    }

    // Excluded activity types
    activityController.excludedActivityTypes = [
        .airDrop,
        .print,
        .assignToContact,
        .addToReadingList,
    ]

    // Present
    DispatchQueue.main.async {
        viewController.present(activityController, animated: true)
    }

    // Completion handler
    activityController.completionWithItemsHandler = { (activity, success, items, error) in
        guard success else {
            if let error = error {
                print(error.localizedDescription)
            }
            return
        }

            // do something if needed
       }
   } 
}
// MARK: - Activity Controller Items

/**
 ActivityControllerItems
 */
private final class ActivityControllerItems: NSObject {

    // MARK: - Properties

    /// App name
    fileprivate let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "-"

    /// App store web url
    fileprivate let appStoreURL: String

    // MARK: - Init

    /// Init
    fileprivate init(appStoreURL: String) {
        self.appStoreURL = appStoreURL
        super.init()
    }
}

// MARK: - UIActivityItemSource

/// UIActivityItemSource
extension ActivityControllerItems: UIActivityItemSource {

    /// Getting data items

    /// Placeholder item
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return ""
    }

    /// Item for actity type
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        return URL(string: appStoreURL) ?? appName
    }

    /// Provide info about data items

    /// Subject field for services such as email
    func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
        return appName
    }
}

      



Then when the share button is clicked you can call it like this

 ShareMenu.open(
     text: "Can you beat my score?", 
     image: UIImage(...),  // set to nil if unused
     appStoreURL: "your iTunes app store URL",  // set to nil if unused
     from: view?.window?.rootViewController
 )

      

Remember the image and appStoreURL will not show up all over the place, it depends on the sharing service.

You can also use your rating value from your scene and add it to the text like

ShareMenu.open( 
     text: "Can you beat my score \(self.score)?",
     ...
)

      

Hope it helps

+2


source


As I said, this can be done using notifications, for example in this answer , or you can go with a delegation:

First, you must declare a protocol MyDelegate

that defines one method called myMethod()

.

 protocol MyDelegate:class {

        func myMethod()
    }

      

This method is a requirement that every class must fulfill if it conforms to this protocol.

In our example, you can view the scene as a worker, and the view controller as a boss. When the scene finishes its task, it notifies its boss (delegates responsibilities to him) about the completion of the work so that the boss can decide what comes next. I mean, I can say, "The scene is the boss, and it delegates to its employee, the viewer ..." But it doesn't really matter who you think is the boss ... the delegation parameter does matter.

So the view controller must conform to this protocol and will implement myMethod()

(which will be called by the scene later):

class GameViewController: UIViewController, MyDelegate {

override func viewDidLoad() {
    super.viewDidLoad()

    //MARK: Conforming to MyDelegate protocol

    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.sks'
        if let scene = GameScene(fileNamed: "GameScene") {
            // Set the scale mode to scale to fit the window
            scene.scaleMode = .aspectFill

            scene.myDelegate = self

            // Present the scene
            view.presentScene(scene)
        }
    }
   }

func myMethod(){
    print("Do your stuff here")
}


 }

      

And here's the code from GameScene

where you define the property MyDelegate

we use to communicate with our view controller:

import SpriteKit

class GameScene: SKScene {


   weak var myDelegate:MyDelegate?



override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


    self.myDelegate?.myMethod()

    }
}

      

To find out when to choose delegation over notifications and vice versa, have a look in this article (or just search SO, there are some good posts on this).

0


source







All Articles