ContentView.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import UIKit
  15. import Combine
  16. import SwiftUI
  17. import FirebaseCore
  18. import FirebaseMessaging
  19. import FirebaseInstanceID
  20. import FirebaseInstallations
  21. struct ContentView: View {
  22. @EnvironmentObject var identity: Identity
  23. @EnvironmentObject var settings: UserSettings
  24. @State private var log: String = ""
  25. var body: some View {
  26. NavigationView {
  27. // Outer stack containing the list and the buttons.
  28. VStack {
  29. List {
  30. VStack(alignment: .leading) {
  31. Text("InstanceID")
  32. .font(.subheadline)
  33. .fontWeight(.semibold)
  34. Text(identity.instanceID ?? "None").foregroundColor(.green)
  35. }
  36. VStack(alignment: .leading) {
  37. Text("Token")
  38. .font(.subheadline)
  39. .fontWeight(.semibold)
  40. Text(identity.token ?? "None")
  41. .foregroundColor(.green)
  42. // Increase the layout priority to allow more than one line to be shown. Without this, the
  43. // simulator renders a single truncated line even though the Preview renders it
  44. // appropriately. Potentially a bug in the simulator?
  45. .layoutPriority(1)
  46. .lineLimit(7)
  47. }
  48. NavigationLink(destination: SettingsView()) {
  49. Text("Settings")
  50. .fontWeight(.semibold)
  51. }
  52. NavigationLink(destination: TopicView()) {
  53. Text("Topic")
  54. .fontWeight(.semibold)
  55. }
  56. // MARK: Action buttons
  57. VStack(alignment: .leading) {
  58. Text("getToken")
  59. .fontWeight(.semibold)
  60. HStack {
  61. Button(action: getIDAndToken) {
  62. HStack {
  63. Image(systemName: "arrow.clockwise.circle.fill")
  64. Text("IID.ID")
  65. .fontWeight(.semibold)
  66. }
  67. }
  68. Button(action: getToken) {
  69. HStack {
  70. Image(systemName: "arrow.clockwise.circle.fill")
  71. Text("IID")
  72. .fontWeight(.semibold)
  73. }
  74. }
  75. Button(action: getFCMToken) {
  76. HStack {
  77. Image(systemName: "arrow.clockwise.circle.fill").font(.body)
  78. Text("FM")
  79. .fontWeight(.semibold)
  80. }
  81. }
  82. }
  83. }.font(.system(size: 14))
  84. VStack(alignment: .leading) {
  85. Text("deleteToken")
  86. .fontWeight(.semibold)
  87. HStack {
  88. Button(action: deleteToken) {
  89. HStack {
  90. Image(systemName: "trash.fill")
  91. Text("IID")
  92. .fontWeight(.semibold)
  93. }
  94. }
  95. Button(action: deleteFCMToken) {
  96. HStack {
  97. Image(systemName: "trash.fill")
  98. Text("FM")
  99. .fontWeight(.semibold)
  100. }
  101. }
  102. }
  103. }.font(.system(size: 14))
  104. VStack(alignment: .leading) {
  105. Text("delete")
  106. .fontWeight(.semibold)
  107. HStack {
  108. Button(action: deleteID) {
  109. HStack {
  110. Image(systemName: "trash.fill")
  111. Text("IID")
  112. .fontWeight(.bold)
  113. }
  114. }
  115. Button(action: deleteFCM) {
  116. HStack {
  117. Image(systemName: "trash.fill")
  118. Text("FM")
  119. .fontWeight(.semibold)
  120. }
  121. }
  122. Button(action: deleteFID) {
  123. HStack {
  124. Image(systemName: "trash.fill")
  125. Text("FIS")
  126. .fontWeight(.semibold)
  127. }
  128. }
  129. }
  130. }.font(.system(size: 14))
  131. Text("\(log)")
  132. .lineLimit(10)
  133. .multilineTextAlignment(.leading)
  134. }
  135. .navigationBarTitle("Firebase Messaging")
  136. }.buttonStyle(IdentityButtonStyle())
  137. }
  138. }
  139. func getIDAndToken() {
  140. InstanceID.instanceID().instanceID { result, error in
  141. guard let result = result, error == nil else {
  142. self.log = "Failed getting iid and token: \(String(describing: error))"
  143. return
  144. }
  145. self.identity.token = result.token
  146. self.identity.instanceID = result.instanceID
  147. self.log = "Successfully got iid and token."
  148. }
  149. }
  150. func getToken() {
  151. guard let app = FirebaseApp.app() else {
  152. return
  153. }
  154. let senderID = app.options.gcmSenderID
  155. var options: [String: Any] = [:]
  156. if Messaging.messaging().apnsToken == nil {
  157. log = "There's no APNS token available at the moment."
  158. return
  159. }
  160. options = ["apns_token": Messaging.messaging().apnsToken as Any]
  161. InstanceID.instanceID()
  162. .token(withAuthorizedEntity: senderID, scope: "*", options: options) { token, error in
  163. guard let token = token, error == nil else {
  164. self.log = "Failed getting token: \(String(describing: error))"
  165. return
  166. }
  167. self.identity.token = token
  168. self.log = "Successfully got token."
  169. }
  170. }
  171. func getFCMToken() {
  172. Messaging.messaging().token { token, error in
  173. guard let token = token, error == nil else {
  174. self.log = "Failed getting iid and token: \(String(describing: error))"
  175. return
  176. }
  177. self.identity.token = token
  178. self.log = "Successfully got token."
  179. print("Token: ", self.identity.token ?? "")
  180. }
  181. }
  182. func deleteFCMToken() {
  183. Messaging.messaging().deleteToken { error in
  184. if let error = error as NSError? {
  185. self.log = "Failed deleting token: \(error)"
  186. return
  187. }
  188. self.log = "Successfully deleted token."
  189. }
  190. }
  191. func deleteToken() {
  192. guard let app = FirebaseApp.app() else {
  193. return
  194. }
  195. let senderID = app.options.gcmSenderID
  196. InstanceID.instanceID()
  197. .deleteToken(withAuthorizedEntity: senderID, scope: "*") { error in
  198. }
  199. }
  200. func deleteID() {
  201. InstanceID.instanceID().deleteID { error in
  202. if let error = error as NSError? {
  203. self.log = "Failed deleting ID: \(error)"
  204. return
  205. }
  206. self.log = "Successfully deleted ID."
  207. }
  208. }
  209. func deleteFCM() {
  210. Messaging.messaging().deleteData { error in
  211. if let error = error as NSError? {
  212. self.log = "Failed deleting Messaging: \(error)"
  213. return
  214. }
  215. self.log = "Successfully deleted Messaging data."
  216. }
  217. }
  218. func deleteFID() {
  219. Installations.installations().delete { error in
  220. if let error = error as NSError? {
  221. self.log = "Failed deleting FID: \(error)"
  222. return
  223. }
  224. self.log = "Successfully deleted FID."
  225. }
  226. }
  227. }
  228. struct ActivityViewController: UIViewControllerRepresentable {
  229. var activityItems: [Any]
  230. var applicationActivities: [UIActivity]? = nil
  231. func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>)
  232. -> UIActivityViewController {
  233. let controller = UIActivityViewController(
  234. activityItems: activityItems,
  235. applicationActivities: applicationActivities
  236. )
  237. return controller
  238. }
  239. func updateUIViewController(_ uiViewController: UIActivityViewController,
  240. context: UIViewControllerRepresentableContext<
  241. ActivityViewController
  242. >) {}
  243. }
  244. struct SettingsView: View {
  245. @EnvironmentObject var settings: UserSettings
  246. @State var shouldUseDelegate = true
  247. @State private var isSharePresented: Bool = false
  248. var body: some View {
  249. VStack {
  250. List {
  251. Toggle(isOn: $settings.isAutoInitEnabled) {
  252. Text("isAutoInitEnabled")
  253. .fontWeight(.semibold)
  254. }
  255. Toggle(isOn: $settings.shouldUseDelegateThanNotification) {
  256. Text("shouldUseDelegate")
  257. .fontWeight(.semibold)
  258. }
  259. Button(action: shareToken) {
  260. HStack {
  261. Image(systemName: "square.and.arrow.up").font(.body)
  262. Text("Share Token")
  263. .fontWeight(.semibold)
  264. }
  265. }
  266. .sheet(isPresented: $isSharePresented, onDismiss: {
  267. print("Dismiss")
  268. }, content: {
  269. let items = [Messaging.messaging().fcmToken]
  270. ActivityViewController(activityItems: items as [Any])
  271. })
  272. }
  273. .font(.subheadline)
  274. .foregroundColor(.blue)
  275. }
  276. }
  277. func shareToken() {
  278. isSharePresented = true
  279. }
  280. }
  281. struct ContentView_Previews: PreviewProvider {
  282. // A fake filled identity for testing rendering of a filled cell.
  283. static let filledIdentity: Identity = {
  284. var identity = Identity()
  285. identity.instanceID = UUID().uuidString
  286. // The token is a long string, generate a very long repeating string of characters to see how the view
  287. // will react.
  288. let longString = UUID().uuidString.replacingOccurrences(of: "-", with: "")
  289. identity.token = Array(repeating: longString, count: 8).reduce("", +)
  290. return identity
  291. }()
  292. static let filledSettings: UserSettings = {
  293. var settings = UserSettings()
  294. settings.shouldUseDelegateThanNotification = true
  295. settings.isAutoInitEnabled = true
  296. return settings
  297. }()
  298. static var previews: some View {
  299. Group {
  300. ContentView().environmentObject(filledIdentity).environmentObject(filledSettings)
  301. }
  302. }
  303. }
  304. struct IdentityButtonStyle: ButtonStyle {
  305. func makeBody(configuration: Self.Configuration) -> some View {
  306. configuration.label
  307. .frame(minWidth: 0, maxWidth: 60)
  308. .padding()
  309. .foregroundColor(.white)
  310. .background(Color.yellow)
  311. .cornerRadius(40)
  312. // Push the button down a bit when it's pressed.
  313. .scaleEffect(configuration.isPressed ? 0.9 : 1)
  314. }
  315. }