Device.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2024 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 Foundation
  15. import FirebaseCore
  16. @_implementationOnly import GoogleUtilities
  17. // TODO: convert to enum
  18. @objc public class Device: NSObject {
  19. static let RCNDeviceContextKeyVersion = "app_version"
  20. static let RCNDeviceContextKeyBuild = "app_build"
  21. static let RCNDeviceContextKeyOSVersion = "os_version"
  22. static let RCNDeviceContextKeyDeviceLocale = "device_locale"
  23. static let RCNDeviceContextKeyLocaleLanguage = "locale_language"
  24. static let RCNDeviceContextKeyGMPProjectIdentifier = "GMP_project_Identifier"
  25. @objc public static func remoteConfigAppVersion() -> String {
  26. return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
  27. }
  28. @objc public static func remoteConfigAppBuildVersion() -> String {
  29. return Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
  30. }
  31. @objc public static func remoteConfigPodVersion() -> String {
  32. return FirebaseVersion()
  33. }
  34. @objc public enum DeviceModel: Int {
  35. case other
  36. case phone
  37. case tablet
  38. case tv
  39. case glass
  40. case car
  41. case wearable
  42. }
  43. @objc public static func remoteConfigDeviceSubtype() -> DeviceModel {
  44. guard let model = GULAppEnvironmentUtil.deviceModel() else {
  45. return .other
  46. }
  47. if model.hasPrefix("iPhone") {
  48. return .phone
  49. }
  50. if model == "iPad" {
  51. return .tablet
  52. }
  53. return .other
  54. }
  55. @objc public static func remoteConfigDeviceCountry() -> String {
  56. return Locale.current.regionCode?.lowercased() ?? ""
  57. }
  58. @objc public static let firebaseLocaleMap = [
  59. // Albanian
  60. "sq": ["sq_AL"],
  61. // Belarusian
  62. "be": ["be_BY"],
  63. // Bulgarian
  64. "bg": ["bg_BG"],
  65. // Catalan
  66. "ca": ["ca", "ca_ES"],
  67. // Croatian
  68. "hr": ["hr", "hr_HR"],
  69. // Czech
  70. "cs": ["cs", "cs_CZ"],
  71. // Danish
  72. "da": ["da", "da_DK"],
  73. // Estonian
  74. "et": ["et_EE"],
  75. // Finnish
  76. "fi": ["fi", "fi_FI"],
  77. // Hebrew
  78. "he": ["he", "iw_IL"],
  79. // Hungarian
  80. "hu": ["hu", "hu_HU"],
  81. // Icelandic
  82. "is": ["is_IS"],
  83. // India
  84. "hi_IN": ["hi_IN", "ta_IN", "te_IN", "mr_IN", "bn_IN", "gu_IN", "kn_IN", "pa_Guru_IN"],
  85. // Indonesian
  86. "id": ["id", "in_ID", "id_ID"],
  87. // Irish
  88. "ga": ["ga_IE"],
  89. // Korean
  90. "ko": ["ko", "ko_KR", "ko-KR"],
  91. // Latvian
  92. "lv": ["lv_LV"],
  93. // Lithuanian
  94. "lt": ["lt_LT"],
  95. // Macedonian
  96. "mk": ["mk_MK"],
  97. // Malay
  98. "ms": ["ms_MY"],
  99. // Maltese
  100. "mt": ["mt_MT"],
  101. // Polish
  102. "pl": ["pl", "pl_PL", "pl-PL"],
  103. // Romanian
  104. "ro": ["ro", "ro_RO"],
  105. // Russian
  106. "ru": ["ru_RU", "ru", "ru_BY", "ru_KZ", "ru-RU"],
  107. // Slovak
  108. "sk": ["sk", "sk_SK"],
  109. // Slovenian
  110. "sl": ["sl_SI"],
  111. // Swedish
  112. "sv": ["sv", "sv_SE", "sv-SE"],
  113. // Turkish
  114. "tr": ["tr", "tr-TR", "tr_TR"],
  115. // Ukrainian
  116. "uk": ["uk", "uk_UA"],
  117. // Vietnamese
  118. "vi": ["vi", "vi_VN"],
  119. // The following are groups of locales or locales that subdivide a
  120. // language).
  121. // Arabic
  122. "ar": [
  123. "ar", "ar_DZ", "ar_BH", "ar_EG", "ar_IQ", "ar_JO", "ar_KW",
  124. "ar_LB", "ar_LY", "ar_MA", "ar_OM", "ar_QA", "ar_SA", "ar_SD",
  125. "ar_SY", "ar_TN", "ar_AE", "ar_YE", "ar_GB", "ar-IQ", "ar_US",
  126. ],
  127. // Simplified Chinese
  128. "zh_Hans": ["zh_CN", "zh_SG", "zh-Hans"],
  129. // Traditional Chinese
  130. // Remove zh_HK until console added to the list. Otherwise client sends
  131. // zh_HK and server/console falls back to zh.
  132. // @"zh_Hant" : @[ @"zh_HK", @"zh_TW", @"zh-Hant", @"zh-HK", @"zh-TW" ],
  133. "zh_Hant": ["zh_TW", "zh-Hant", "zh-TW"],
  134. // Dutch
  135. "nl": ["nl", "nl_BE", "nl_NL", "nl-NL"],
  136. // English
  137. "en": [
  138. "en", "en_AU", "en_CA", "en_IN", "en_IE", "en_MT", "en_NZ", "en_PH",
  139. "en_SG", "en_ZA", "en_GB", "en_US", "en_AE", "en-AE", "en_AS", "en-AU",
  140. "en_BD", "en-CA", "en_EG", "en_ES", "en_GB", "en-GB", "en_HK", "en_ID",
  141. "en-IN", "en_NG", "en-PH", "en_PK", "en-SG", "en-US",
  142. ],
  143. // French
  144. "fr":
  145. [
  146. "fr", "fr_BE", "fr_CA", "fr_FR", "fr_LU", "fr_CH", "fr-CA", "fr-FR", "fr_MA",
  147. ],
  148. // German
  149. "de": ["de", "de_AT", "de_DE", "de_LU", "de_CH", "de-DE"],
  150. // Greek
  151. "el": ["el", "el_CY", "el_GR"],
  152. // Italian
  153. "it": ["it", "it_IT", "it_CH", "it-IT"],
  154. // Japanese
  155. "ja": ["ja", "ja_JP", "ja_JP_JP", "ja-JP"],
  156. // Norwegian
  157. "no": ["nb", "no_NO", "no_NO_NY", "nb_NO"],
  158. // Brazilian Portuguese
  159. "pt_BR": ["pt_BR", "pt-BR"],
  160. // European Portuguese
  161. "pt_PT": ["pt", "pt_PT", "pt-PT"],
  162. // Serbian
  163. "sr": ["sr_BA", "sr_ME", "sr_RS", "sr_Latn_BA", "sr_Latn_ME", "sr_Latn_RS"],
  164. // European Spanish
  165. "es_ES": ["es", "es_ES", "es-ES"],
  166. // Mexican Spanish
  167. "es_MX": ["es-MX", "es_MX", "es_US", "es-US"],
  168. // Latin American Spanish
  169. "es_419": [
  170. "es_AR", "es_BO", "es_CL", "es_CO", "es_CR", "es_DO", "es_EC",
  171. "es_SV", "es_GT", "es_HN", "es_NI", "es_PA", "es_PY", "es_PE",
  172. "es_PR", "es_UY", "es_VE", "es-AR", "es-CL", "es-CO",
  173. ],
  174. // Thai
  175. "th": ["th", "th_TH", "th_TH_TH"],
  176. ]
  177. private static func firebaseLocales() -> [String] {
  178. var locales = [String]()
  179. let localesMap = firebaseLocaleMap
  180. for (_, value) in localesMap {
  181. locales.append(contentsOf: value)
  182. }
  183. return locales
  184. }
  185. @objc public static func remoteConfigDeviceLocale() -> String {
  186. let locales = firebaseLocales()
  187. let preferredLocalizations = Bundle.preferredLocalizations(
  188. from: locales,
  189. forPreferences: Locale.preferredLanguages
  190. )
  191. return preferredLocalizations.first ?? "en"
  192. }
  193. @objc public static func remoteConfigTimezone() -> String {
  194. return TimeZone.current.identifier
  195. }
  196. @objc public static func remoteConfigDeviceContext(with projectIdentifier: String?)
  197. -> [String: String] {
  198. var deviceContext: [String: String] = [:]
  199. deviceContext[RCNDeviceContextKeyVersion] = remoteConfigAppVersion()
  200. deviceContext[RCNDeviceContextKeyBuild] = remoteConfigAppBuildVersion()
  201. deviceContext[RCNDeviceContextKeyOSVersion] = GULAppEnvironmentUtil.systemVersion()
  202. deviceContext[RCNDeviceContextKeyDeviceLocale] = remoteConfigDeviceLocale()
  203. // NSDictionary setObject will fail if there's no GMP project ID, must check ahead.
  204. if let GMPProjectIdentifier = projectIdentifier {
  205. deviceContext[RCNDeviceContextKeyGMPProjectIdentifier] = GMPProjectIdentifier
  206. }
  207. return deviceContext
  208. }
  209. @objc public static func remoteConfigHasDeviceContextChanged(_ deviceContext: [String: String],
  210. projectIdentifier: String?) -> Bool {
  211. guard let version = deviceContext[RCNDeviceContextKeyVersion],
  212. let build = deviceContext[RCNDeviceContextKeyBuild],
  213. let osVersion = deviceContext[RCNDeviceContextKeyOSVersion],
  214. let locale = deviceContext[RCNDeviceContextKeyDeviceLocale] else {
  215. return true
  216. }
  217. if version != remoteConfigAppVersion() ||
  218. build != remoteConfigAppBuildVersion() ||
  219. osVersion != GULAppEnvironmentUtil.systemVersion() ||
  220. locale != remoteConfigDeviceLocale() {
  221. return true
  222. }
  223. if let gmpProjectIdentifier = deviceContext[RCNDeviceContextKeyGMPProjectIdentifier] {
  224. return gmpProjectIdentifier != projectIdentifier
  225. }
  226. return false
  227. }
  228. }