Files
firebase-ios-sdk/Example/Messaging/App/iOS/MessagingViewController.swift
T

332 lines
11 KiB
Swift
Raw Normal View History

2017-05-15 12:27:07 -07:00
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import UIKit
import FirebaseCommunity
2017-05-15 12:27:07 -07:00
enum Row: String {
case apnsToken = "apnsToken"
case apnsStatus = "apnsStatus"
case requestAPNSPermissions = "requestAPNSPermissions"
case fcmToken = "fcmToken"
}
enum PermissionsButtonTitle: String {
case requestPermissions = "Request User Notifications"
case noAPNS = "Cannot Request Permissions (No APNs)"
case alreadyRequested = "Already Requested Permissions"
case simulator = "Cannot Request Permissions (Simulator)"
}
class MessagingViewController: UIViewController {
let tableView: UITableView
var sections = [[Row]]()
var sectionHeaderTitles = [String?]()
var allowedNotificationTypes: [NotificationsControllerAllowedNotificationType]?
// Cached rows by Row type. Since this is largely a fixed table view, we'll
// keep track of our created cells and UI, rather than have all the logic
required init?(coder aDecoder: NSCoder) {
tableView = UITableView(frame: CGRect.zero, style: .grouped)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
// Allow UI Controls within the table to be immediately responsive
tableView.delaysContentTouches = false
super.init(coder: aDecoder)
tableView.dataSource = self
tableView.delegate = self
}
override func loadView() {
super.loadView()
view = UIView(frame: CGRect.zero)
view.addSubview(self.tableView)
// Ensure that the tableView always is the size of the view
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
override func viewDidLoad() {
super.viewDidLoad()
let center = NotificationCenter.default
center.addObserver(self,
selector: #selector(onAPNSTokenReceived) ,
name: APNSTokenReceivedNotification,
object: nil)
center.addObserver(self,
selector: #selector(onUserNotificationSettingsChanged),
name: UserNotificationsChangedNotification,
object: nil)
center.addObserver(self,
selector: #selector(onFCMTokenRefreshed),
name: Notification.Name.MessagingRegistrationTokenRefreshed,
object: nil)
updateAllowedNotificationTypes {
self.resetTableContents()
self.tableView.reloadData()
}
}
func onAPNSTokenReceived() {
// Reload the appropriate cells
updateAllowedNotificationTypes {
if let tokenPath = self.indexPathFor(.apnsToken),
let statusPath = self.indexPathFor(.apnsStatus),
let requestPath = self.indexPathFor(.requestAPNSPermissions) {
self.updateIndexPaths(indexPaths: [tokenPath, statusPath, requestPath])
}
}
}
func onFCMTokenRefreshed() {
if let indexPath = indexPathFor(.fcmToken) {
updateIndexPaths(indexPaths: [indexPath])
}
}
func onUserNotificationSettingsChanged() {
updateAllowedNotificationTypes {
if let statusPath = self.indexPathFor(.apnsStatus),
let requestPath = self.indexPathFor(.requestAPNSPermissions) {
self.updateIndexPaths(indexPaths: [statusPath, requestPath])
}
}
}
private func updateIndexPaths(indexPaths: [IndexPath]) {
tableView.beginUpdates()
tableView.reloadRows(at: indexPaths, with: .none)
tableView.endUpdates()
}
fileprivate func updateAllowedNotificationTypes(_ completion: (() -> Void)?) {
NotificationsController.shared.getAllowedNotificationTypes { (types) in
self.allowedNotificationTypes = types
self.updateRequestAPNSButton()
completion?()
}
}
fileprivate func updateRequestAPNSButton() {
guard !Environment.isSimulator else {
requestPermissionsButton.isEnabled = false
requestPermissionsButton.setTitle(PermissionsButtonTitle.simulator.rawValue, for: .normal)
return
}
guard let allowedTypes = allowedNotificationTypes else {
requestPermissionsButton.isEnabled = false
requestPermissionsButton.setTitle(PermissionsButtonTitle.noAPNS.rawValue, for: .normal)
return
}
requestPermissionsButton.isEnabled =
(allowedTypes.count == 1 && allowedTypes.first! == .silent)
let title: PermissionsButtonTitle =
(requestPermissionsButton.isEnabled ? .requestPermissions : .alreadyRequested)
requestPermissionsButton.setTitle(title.rawValue, for: .normal)
}
// MARK: UI (Cells and Buttons) Defined as lazy properties
lazy var apnsTableCell: UITableViewCell = {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.apnsToken.rawValue)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = .byWordWrapping
return cell
}()
lazy var apnsStatusTableCell: UITableViewCell = {
let cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: Row.apnsStatus.rawValue)
cell.textLabel?.text = "Allowed:"
cell.detailTextLabel?.numberOfLines = 0
cell.detailTextLabel?.lineBreakMode = .byWordWrapping
return cell
}()
lazy var requestPermissionsButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle(PermissionsButtonTitle.requestPermissions.rawValue, for: .normal)
button.setTitleColor(UIColor.gray, for: .highlighted)
button.setTitleColor(UIColor.gray, for: .disabled)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
button.addTarget(self,
action: #selector(onRequestUserNotificationsButtonTapped),
for: .touchUpInside)
return button
}()
lazy var apnsRequestPermissionsTableCell: UITableViewCell = {
let cell = UITableViewCell(style: .default,
reuseIdentifier: Row.requestAPNSPermissions.rawValue)
cell.selectionStyle = .none
cell.contentView.addSubview(self.requestPermissionsButton)
self.requestPermissionsButton.frame = cell.contentView.bounds
self.requestPermissionsButton.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return cell
}()
lazy var fcmTokenTableCell: UITableViewCell = {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.fcmToken.rawValue)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = .byCharWrapping
return cell
}()
}
// MARK: - Configuring the table view and cells with information
extension MessagingViewController {
func resetTableContents() {
sections.removeAll()
sectionHeaderTitles.removeAll()
// APNS
let apnsSection: [Row] = [.apnsToken, .apnsStatus, .requestAPNSPermissions]
sections.append(apnsSection)
sectionHeaderTitles.append("APNs")
// FCM
let fcmSection: [Row] = [.fcmToken]
sections.append(fcmSection)
sectionHeaderTitles.append("FCM Token")
}
func indexPathFor(_ rowId: Row) -> IndexPath? {
var sectionIndex = 0
for section in sections {
var rowIndex = 0
for row in section {
if row == rowId {
return IndexPath(row: rowIndex, section: sectionIndex)
}
rowIndex += 1
}
sectionIndex += 1
}
return nil
}
func configureCell(_ cell: UITableViewCell, withAPNSToken apnsToken: Data?) {
guard !Environment.isSimulator else {
cell.textLabel?.text = "APNs notifications are not supported in the simulator."
cell.detailTextLabel?.text = nil
return
}
if let apnsToken = apnsToken {
cell.textLabel?.text = apnsToken.hexByteString
cell.detailTextLabel?.text = "Tap to Share"
} else {
cell.textLabel?.text = "None"
cell.detailTextLabel?.text = nil
}
}
func configureCellWithAPNSStatus(_ cell: UITableViewCell) {
if let allowedNotificationTypes = allowedNotificationTypes {
let displayableTypes: [String] = allowedNotificationTypes.map { return $0.rawValue }
cell.detailTextLabel?.text = displayableTypes.joined(separator: ", ")
} else {
cell.detailTextLabel?.text = "Retrieving..."
}
}
func configureCell(_ cell: UITableViewCell, withFCMToken fcmToken: String?) {
if let fcmToken = fcmToken {
cell.textLabel?.text = fcmToken
cell.detailTextLabel?.text = "Tap to Share"
} else {
cell.textLabel?.text = "None"
cell.detailTextLabel?.text = nil
}
}
}
// MARK: - UITableViewDataSource
extension MessagingViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
public func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = sections[indexPath.section][indexPath.row]
let cell: UITableViewCell
switch row {
case .apnsToken:
cell = apnsTableCell
configureCell(cell, withAPNSToken: Messaging.messaging().apnsToken)
case .apnsStatus:
cell = apnsStatusTableCell
configureCellWithAPNSStatus(cell)
case .requestAPNSPermissions:
cell = apnsRequestPermissionsTableCell
case .fcmToken:
cell = fcmTokenTableCell
configureCell(cell, withFCMToken: Messaging.messaging().fcmToken)
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionHeaderTitles[section]
}
}
// MARK: - UITableViewDelegate
extension MessagingViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let row = sections[indexPath.section][indexPath.row]
switch row {
case .apnsToken:
if let apnsToken = Messaging.messaging().apnsToken {
showActivityViewControllerFor(sharedItem: apnsToken.hexByteString)
}
case .fcmToken:
if let fcmToken = Messaging.messaging().fcmToken {
showActivityViewControllerFor(sharedItem: fcmToken)
}
default: break
}
}
}
// MARK: - UI Controls
extension MessagingViewController {
func onRequestUserNotificationsButtonTapped(sender: UIButton) {
NotificationsController.shared.registerForUserFacingNotificationsFor(UIApplication.shared)
}
}
// MARK: - Activity View Controller
extension MessagingViewController {
func showActivityViewControllerFor(sharedItem: Any) {
let activityViewController = UIActivityViewController(activityItems: [sharedItem],
applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
}
}