Skip to content

Commit

Permalink
Add GamingWebDialog
Browse files Browse the repository at this point in the history
Summary: Creates a general-purpose `_WebDialog` wrapper for `fb.gg`.

Reviewed By: joesus

Differential Revision: D39452083

fbshipit-source-id: 48f0e76c6e02739c219633c40bf24af7c983e154
  • Loading branch information
JarWarren authored and facebook-github-bot committed Oct 6, 2022
1 parent 43cfd8b commit 4c890ed
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 2 deletions.
7 changes: 5 additions & 2 deletions FBSDKCoreKit/FBSDKCoreKit/Internal/WebDialog/_WebDialog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public final class _WebDialog: NSObject {
var parameters: [String: String]?
var backgroundView: UIView?
var dialogView: FBWebDialogView?
var path: String?

private enum AnimationDuration {
static let show = 0.2
Expand All @@ -50,11 +51,13 @@ public final class _WebDialog: NSObject {
public init(
name: String,
parameters: [String: String]?,
webViewFrame: CGRect = .zero
webViewFrame: CGRect = .zero,
path: String? = nil
) {
self.name = name
self.parameters = parameters
self.webViewFrame = webViewFrame
self.path = path
}

public convenience init(name: String) {
Expand Down Expand Up @@ -163,7 +166,7 @@ public final class _WebDialog: NSObject {
}
return try InternalUtility.shared.facebookURL(
hostPrefix: "m",
path: "/dialog/\(name)",
path: path ?? "/dialog/\(name)",
queryParameters: urlParameters
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

// swiftlint:disable:all prefer_final_classes

#if !os(tvOS)

import FBSDKCoreKit
import Foundation

/**
General-purpose web dialog for presenting `fb.gg/dialog/{view}`
- warning: INTERNAL - DO NOT USE. This class is public so that other public types may extend it.
*/
public class GamingWebDialog<Success: GamingWebDialogSuccess>: WebDialogDelegate {
private enum Keys {
static var path: String { "/gaming/dialog/" }
static var errorCode: String { "error_code" }
static var errorMessage: String { "error_message" }
}

public var completion: ((Result<Success, Error>) -> Void)?
var dialog: _WebDialog?
var parameters = [String: String]()
var frame = CGRect.zero
let name: String

init(name: String) {
self.name = name
}

func show(completion: @escaping (Result<Success, Error>) -> Void) {
self.completion = completion
dialog = _WebDialog(
name: name,
parameters: parameters,
webViewFrame: frame,
path: Keys.path + name
)
dialog?.delegate = self
InternalUtility.shared.registerTransientObject(self)
dialog?.show()
}

public func webDialog(_ webDialog: _WebDialog, didCompleteWithResults results: [String: Any]) {
guard webDialog == dialog else {
if let dialog = dialog {
let error = _ErrorFactory().unknownError(message: "The dialog failed to retrieve results.")
self.webDialog(dialog, didFailWithError: error)
}
return
}

if
let errorCode = results[Keys.errorCode] as? Int,
let errorMessage = results[Keys.errorMessage] as? String {
let errorFactory = _ErrorFactory()
let error = errorFactory.error(
code: errorCode,
userInfo: nil,
message: errorMessage,
underlyingError: nil
)
completion?(.failure(error))
completion = nil
return
}

completion?(.success(Success(results)))
completion = nil
}

public func webDialog(_ webDialog: _WebDialog, didFailWithError error: Error) {
guard webDialog == dialog else {
return
}
completion?(.failure(error))
InternalUtility.shared.unregisterTransientObject(self)
}

public func webDialogDidCancel(_ webDialog: _WebDialog) {
guard webDialog == dialog else {
return
}
completion?(.success(Success([:])))
InternalUtility.shared.unregisterTransientObject(self)
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

import Foundation

/**
The result of a completed `GamingWebDialog`
- warning: INTERNAL - DO NOT USE
*/
public protocol GamingWebDialogSuccess {
init(_ dict: [String: Any])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/

@testable import FBSDKCoreKit
@testable import FBSDKGamingServicesKit
import TestTools
import XCTest

final class GamingWebDialogTests: XCTestCase {

struct Success: GamingWebDialogSuccess {
let payload: String?

init(_ dict: [String: Any]) {
payload = dict["payload"] as? String
}
}

let payloadKey = "payload"
let errorCodeKey = "error_code"
let errorMessageKey = "error_message"

// swiftlint:disable:next implicitly_unwrapped_optional
var gamingWebDialog: GamingWebDialog<Success>!
// swiftlint:disable:next implicitly_unwrapped_optional
var webDialog: _WebDialog!

var webDelegate: WebDialogDelegate { gamingWebDialog as WebDialogDelegate }
var dialogCompleted = false
var dialogFailed = false
var error: Error?
var payload: String?

override func setUp() {
super.setUp()
error = nil
payload = nil
dialogCompleted = false
dialogFailed = false

gamingWebDialog = GamingWebDialog(name: "test")

webDialog = _WebDialog(name: "Test")
webDialog.delegate = webDelegate
gamingWebDialog.dialog = webDialog
gamingWebDialog.completion = { result in
switch result {
case let .success(success):
self.dialogCompleted = true
self.payload = success.payload
case let .failure(error):
self.dialogFailed = true
self.error = error
}
}
}

func testDialogSucceedsWithPayload() throws {
let testPayload = "test payload"

gamingWebDialog.webDialog(webDialog, didCompleteWithResults: [payloadKey: testPayload])

XCTAssertTrue(dialogCompleted)
XCTAssertFalse(dialogFailed)
XCTAssertNil(error)
XCTAssertEqual(payload, testPayload)
}

func testDialogFailsWithError() throws {
let testErrorCode = 418
let testErrorMessage = "I'm a teapot"

gamingWebDialog.webDialog(
webDialog,
didCompleteWithResults: [errorCodeKey: testErrorCode, errorMessageKey: testErrorMessage]
)
let error = try XCTUnwrap(error) as NSError

XCTAssertFalse(dialogCompleted)
XCTAssertTrue(dialogFailed)
XCTAssertNotNil(error)
XCTAssertEqual(error.code, testErrorCode)
XCTAssertEqual(error.userInfo.values.first as? String, testErrorMessage)
}

func testDialogFailsWithUnknownError() throws {
gamingWebDialog.webDialog(
_WebDialog(name: "wrong dialog"),
didCompleteWithResults: [:]
)
gamingWebDialog.show { _ in }
let error = try XCTUnwrap(error) as NSError

XCTAssertFalse(dialogCompleted)
XCTAssertTrue(dialogFailed)
XCTAssertNotNil(error)
}

func testDialogSucceedsEmptyWhenCanceled() throws {
gamingWebDialog.webDialogDidCancel(webDialog)

XCTAssertTrue(dialogCompleted)
XCTAssertFalse(dialogFailed)
XCTAssertNil(payload)
XCTAssertNil(error)
}
}

0 comments on commit 4c890ed

Please sign in to comment.