Firebase 远程配置 iOS 教程

原文:Firebase Remote Config Tutorial for iOS

作者:Todd Kerpelman

译者:kmyhy

记得发布 App 的时候吗?App 每个方面都已经做到最好了吗?你永远不需要碰别的代码了,因为在第一次提交时你就已经做到完美无缺了?

不,我不敢说。

事实是,作为一个功成名就的开发者,通常意味着对 App 没完没了地修改。有时候这种修改是为了增加功能或修复 Bug。但有时候,影响最大的更新无非是一行代码的事儿,比如调整某段文字,或者降低某个塔防游戏中能量单位。

这些改变都很轻松,但发布它们仍然不得不等待许多天。有什么好办法能够做一些细微的修改而不用完成整个流程?

Firebase 远程配置带给你这种方便。在这篇 Firebase 远程配置教程中,你会拿一个 Planet Tour App 为例,学习如何实时修改文字、颜色及其他属性,而不需要发布新包。熟悉之后,你可以使用一些更强大的特性,比如针对不同用户发布不同的内容套装。

前提:本文假设你熟悉和安装了 CocoaPods。否则,请参考我们的 CocoaPods 教程

运行示例 App

在开始本教程的学习之前,请下载和运行 Planet Tour Starter app。你看可以转动视图,观察不同的行星,并点击它们获得详细数据(非常精确)。

在 Planet Tour Apps 的公司,App 运行得非常良好,直到某天市场部的 Greg 决定将 Planet Tour 换成绿色主题以庆祝地球日。(呃,你可以想象一下,你正在地球日即将来临前的某一天阅读本文。)

这个真的太简单了——如果你看一眼 AppConstants.swift,那里有一个appPrimaryColor 变量,你可以修改它,它会影响所有文字标签的颜色。将这个更新推给你的用户可能需要发布一个新包,提交商店,通过 App 评审,祈祷你所有的用户会在地球日之前下载它。等地球日一过,你又得全部重来一遍。

如果能够从云端改变这些值就好了!

安装远程配置库

这正好是 Firebase 远程配置的最佳案例。冲进附近的时光机,让我们来代替你做出决定,将你的 App 用远程配置取代 AppConstants 中的硬编码。

首先,你需要在 Firebase 控制台中创建一个项目,将它和 Planet Tour App 关联上,然后安装 Firebase 远程配置库。

让我们一步步来:

  1. 打开 firebase.google.com/console
  2. 点击创建新项目。
  3. 将项目命名为 Plannet Tour,确保选择你所在的地区,然后点击创建项目。

  4. 然后,点击 Add Firebase to your iOS app。

  5. 填入你的 Bundle Id(com.razeware.Planet-Tour) ,App Store ID 一栏保留为空。然后点 Add App。

  6. 这时,浏览器会下载 GoogleServices-info.plist 文件。将这个文件拖到 Xcode 项目中(选中 Copy Items if Needed)。
  7. 剩下来的步骤就是在设置向导中点击 Continue。(别担心,后面我们会带你完成这个步骤)。
  8. 在 Xcode 中关闭 Planet Tour 项目。
  9. 打开终端,进入项目目录,输入 pod init 创建一个基本的 Podfile 文件。
  10. 编辑 Podfile :
    target ‘Planet Tour‘ do
    
    # Comment this line if you‘re not using Swift and don‘t want to use dynamic frameworks
    
    use_frameworks!
    
    # Pods for Planet Tour
    
    pod ‘Firebase/Core‘
    pod ‘Firebase/RemoteConfig‘
    end
  11. 执行 pod install,然后用 Xcode 打开 Planet Tour.xcworkspace in Xcode。
  12. 打开 AppDelegate.swift,在 import UIKit 后面添加:
    import Firebase

    然后在 application(_:didFinishLaunchingWithOptions:) 方法返回语句之前添加:

    FIRApp.configure()

    这句代码会找到你安装的库,并用添加 GoogleServices-info.plist 时导入到项目的常量来配置它。远程配置库知道在互联网的某个地方来找到新的值。

运行程序。程序看起来和先前没有任何区别,但在控制台中你会看到一些之前没有见过的调试信息。

注:你可能会看到类似 FIRInstanceID/WARNING STOP!! Will reset deviceID from memory 这样的错误。在 Xcode 8 中,你需要在试用 FireBase 的时候开启钥匙串共享即可消除这个错误。

关系你!你已经安装好了远程配置库!在接下来的教程中你可以使用它了。

使用远程配置

如果你想知道远程配置是如何工作的,最简单的办法是把它看成是一个放在云端的 NSDictionary。当 App 启动时,它会从云端获取新值,并在应用你默认指定的旧值之前优先使用它们。远程配置的正常工作流程如下:

  1. 以 defaults 形式将你有可能在未来改变的值提供给远程配。
  2. 从云端抓取新值。这些会保存到设备的缓存中。
  3. “激活”这些值。你可以看成,抓取到的值被应用到原来的 defaults 值上。
  4. 向远程配置索要值。远程配置要么给你从云端获取到的值(如果有的话),要么给你默认值。

]

值得注意的一点是,从云端抓取的新值通常是你指定的默认值的子集。你可以使用 App 中任何硬编码的字符串、数字或布尔值,并将它们用远程配置连接起来。这种方式很灵活,你可以在未来改变 App 的任何一个方面,但保持你的网络接口整洁、灵巧。

课本念完了。接下来要进行实际操作。

首先,在 Xcode 中打开 Utilities 文件夹,用右键创建新文件。选择 Swift 文件,就叫做 RCValues.swift,将它保存在 Xcode 默认的文件夹。

编辑它的内容为:

import Foundation
import Firebase

class RCValues {

  static let sharedInstance = RCValues()

  private init() {
    loadDefaultValues()
  }

  func loadDefaultValues() {
    let appDefaults: [String: NSObject] = [
      "appPrimaryColor" : "#FBB03B" as NSObject
    ]
    FIRRemoteConfig.remoteConfig().setDefaults(appDefaults)
  }
}

RCValues 类使用了单例模式。在 loadDefaultValues() 方法中,你将一个键值对以 defaults 的形式传递给远程配置。暂时你只提供了一个值,但无需担心,后面会添加更多的值。

然后,让远程配置从云端抓取新值。在 init 方法的最后加入:

fetchCloudValues()

在 loadDefaultValues() 方法后新增方法,用于抓取新值:

func fetchCloudValues() {
  // 1
  // WARNING: Don‘t actually do this in production!
  let fetchDuration: TimeInterval = 0
  FIRRemoteConfig.remoteConfig().fetch(withExpirationDuration: fetchDuration) {
    [weak self] (status, error) in

    guard error == nil else {
      print ("Uh-oh. Got an error fetching remote values \(error)")
      return
    }

    // 2
    FIRRemoteConfig.remoteConfig().activateFetched()
    print ("Retrieved values from the cloud!")
  }
}

代码解释如下:

  1. 默认,远程配置会缓存从云端获取的值大约 12 个小时。在真实 App 中,这可能够了。但对于你现在要干的事情来说(或者后续一个远程配置教程),这就麻烦大了。因此,你在这里指定了 fetchDuration 为 0,这样永远不会采用缓存数据。
  2. 在完成闭包中,你立即激活这些新抓取到的值,例如,你告诉远程配置,如果一旦发现有新值,立即覆盖旧值。

现在,你在一开始加入的代码出现一点问题。远程配置库有一个客户端的限制,避免你频繁地 ping 服务器。因为你将 fetchDuration 设为了 0,这就违反了这个限制,库会停止调用。

启用开发者模式可以解决这个问题。在 fetchCloudValues() 项目添加方法:

func activateDebugMode() {
  let debugSettings = FIRRemoteConfigSettings(developerModeEnabled: true)
  FIRRemoteConfig.remoteConfig().configSettings = debugSettings!
}

通过设置开发者模式为 true,你告诉远程配置忽略客户端限制。对于测试使用,或者在 10 个人范围内进行测试,这就够了。但如果你将 App 放到公网上,给你的成千上万的粉丝用,你很快就违反了服务端限制,远程库会停止工作。(这就是首先需要在客户端进行一个限制的原因)。

无论如何,请你在将 App 推到生产之前,一定要关闭开发者模式,并设置你的 fetchDuration 为一个合理的值,比如 43200(即 12 小时)。

最后,在设置完 fetchDuration 之后添加:

activateDebugMode()

这句激活调试模式,你不会再出现服务端限制问题。

好,让代码跑起来。打开 AppDelegate.swift, 在 application(_:didFinishLaunchingWithOptions:) 方法中, FIRApp.configure() 一句的后面添加:

let _ = RCValues.sharedInstance

运行程序,在控制台中你会看到:

Retrieved values from the cloud!

注:写到这里的时候,在 Xcode 8 模拟器中,花了很长时间(大概 45 秒)才打出这句。如果你在真机上,速度会快得多。

使用远程配置的值

你已经下到了这些值,将它们打印出来吧。在 fetchCloudValues() 方法的打印 “Retrieved values from the cloud” 的一行后添加:

print ("Our app‘s primary color is
    \(FIRRemoteConfig.remoteConfig().configValue(forKey: "appPrimaryColor"))")

这将获取 appPrimaryColor 的值。

运行程序,你会看到:

Our app‘s primary color is <FIRRemoteConfigValue: 0x61000003ece0>

呃,这其实没什么鸟用,你其实更希望获得一个字符串值。

远程配置将收到的值封装为 FIRRemoteConfigValue 对象,可以看成是某种底层数据的封装,本质上是一种 UTF8 编码的字符串)。你几乎不会直接使用这种类型,而是调用 numberValue/boolValue 之类的助手方法转为你想要的类型。

将刚刚添加的这行为:

print ("Our app‘s primary color is
    \(FIRRemoteConfig.remoteConfig().configValue(forKey: "appPrimaryColor").stringValue)")

运行程序,这次你会看到:

Our app‘s primary color is Optional("#FBB03B")

这才像话,远程配置把你在前面配置的 default 值提供给你了。

修改云端值

现在,你从远处配置获得了正确的值,让我们来试一下修改云端的值。

打开 Firebase 控制台,点击 header 左边的 Remote Config。点 Add your first parameter。在表单中输入一个键叫做 appPrimaryColor,值输入市场部的 Greg 指定的绿色 — #36C278。

点 Add Parameter, 再点 Publish Changes 去更新修改。

运行程序,看一眼控制台:

Our app’s primary color is Optional("#36C278")

哇,现在你已经从云端修改了值!

修改 App 的样式和风格

Xcode 控制台的最新输出确实让我们激动了好一会儿,你的读者们则要理智的多。让我们来看看如何在 App 中应用新值。

首先,加一个每句类型,用于表示这些键吧。直接用字符串作为 key 简直是一场灾难——它至少会让你花费一个下午才能够找出拼写错误的键名。通过枚举,Xcode 会在编译时提示错误,而不是要等到运行时。

打开 RCValues.swift 在类定义之前加入:

enum ValueKey: String {
  case appPrimaryColor
}

然后,修改 loadDefaultValues() 方法以使用新枚举替换字符串常量:

let appDefaults: [String: NSObject] = [
  ValueKey.appPrimaryColor.rawValue : "#FBB03B" as NSObject
]

为 RCValues 添加助手方法,接受一个 ValueKey 参数并返回从远程配置中获取到的颜色:

func color(forKey key: ValueKey) -> UIColor {
  let colorAsHexString = FIRRemoteConfig.remoteConfig()[key.rawValue].stringValue ?? "#FFFFFFFF"
  let convertedColor = UIColor(rgba: colorAsHexString)
  return convertedColor
}

最后,将 App 中使用到 AppConstants 值的地方用新的 RCValues 助手方法替代。总共有三个地方:

  1. 打开 ContainerViewController.swift, 在 updateBanner() 方法中,将:

    bannerView.backgroundColor = AppConstants.appPrimaryColor
    

    修改为:

    bannerView.backgroundColor = RCValues.sharedInstance.color(forKey: .appPrimaryColor)
    
  2. 打开 GetNewsletterViewController.swift, 在 updateSubmitButton() 方法中,将:
    submitButton.backgroundColor = AppConstants.appPrimaryColor
    

    修改为:

    submitButton.backgroundColor = RCValues.sharedInstance.color(forKey: .appPrimaryColor)
    
  3. 打开 PlanetDetailViewController.swift, 在 updateLabelColors() 方法中,将:
    nextLabel.textColor = AppConstants.appPrimaryColor
    

    修改为:

    nextLabel.textColor = RCValues.sharedInstance.color(forKey: .appPrimaryColor)
    

    最后, 打开 AppConstants.swift 删掉这句:

    static let appPrimaryColor = UIColor(rgba: “#36C278”)

拜拜了,硬编码……

运行程序,你会看到你的 App 现在是绿色了。

在应用这些新值的时候,你没有多少控制权。第一次运行 App 时,你会看见主菜单是默认的橙色,当你从云端加载新值之后,行星详情显示为绿色。

这可能会给用户带来困惑。在这种情况下,你只改变了部分 Label 的颜色,如果用户正在使用中而 App 恰巧正在改变颜色或者某些会影响到它的行为的值时,用户会更奇怪。

有无数种解决办法。但最简单的方法是使用一个 loading 界面。幸运的是,已经有一个现成的(至少是部分)摆在那儿了。

使用 Loading 页

首先你需要让 Loading 页面作为 App 的第一个 View Controller。打开 Main.storyboard ,右键,从你的导航控制器拖到 Waiting View Controller — 这个 View Controller 有一个黑背景,当然你也可以在故事版的 Outline 窗口中来右键拖放,这个更容易些。在弹出菜单中选择 root view controller:这将使你的 Loading 页变成 App 启动时的第一个画面。

现在,来加一些代码使得当远程配置完成抓取后跳转到主菜单页面。

打开 RCValues.swift, 在 sharedInstance 属性后添加:

var loadingDoneCallback: (() -> ())?
var fetchComplete: Bool = false

在 fetchCloudValues() 完成块的最后加入两行代码:

func fetchCloudValues() {
 // 警告: 不要在生产项目中这样做!
 let fetchDuration : TimeInterval = 0
 activateDebugMode()
 FIRRemoteConfig.remoteConfig().fetch(withExpirationDuration: fetchDuration) {
   [weak self] (status, error) in

   guard error != nil else {
     print ("Uh-oh. Got an error fetching remote values \(error)")
     return
   }   

   FIRRemoteConfig.remoteConfig().activateFetched()
   print ("Retrieved values from the cloud!")
   print ("Our app‘s primary color is
     \(FIRRemoteConfig.remoteConfig().configValue(forKey: "appPrimaryColor").stringValue)")

   // 在这里加入两行!
   self?.fetchComplete = true
   self?.loadingDoneCallback?()
 }
}

这里,当抓取完成时,将 fetchComplete 变量设为 true。然后调用回调块通知监听者远程配置新值已经抓取完了。这用于告诉 Loading 页面将自己解散。

打开 WaitingViewController.swift 新增方法:

func startAppForReal() {
  performSegue(withIdentifier: "loadingDoneSegue", sender: self)
}
将 viewDidLoad() 替换为:

```swift
override func viewDidLoad() {
  super.viewDidLoad()

  if RCValues.sharedInstance.fetchComplete {
    startAppForReal()
  }

  RCValues.sharedInstance.loadingDoneCallback = startAppForReal
}

<div class="se-preview-section-delimiter"></div>

这里,当所有值都已经抓取完成后,调用了 stratAppFroReal() 方法。这里也进行了另一种判断,即在 Loading 屏已经被加载之前,RCValues 是否已经完成了网络抓取。这种情况其实是不可能发生的,但加上它只会让你的代码更健壮。

我有一个编码规则:当你在代码中注释“这种情况永远不会发生”的时候,恰恰表明未来很可能就会发生。

运行程序。你会看到 Loading 屏会短暂显示(取决于你的网速),然后就跳到 App 的其他界面了。如果你修改了 App 的主题颜色并重启 App,这个颜色就会在整个 App 中都得到正确应用了。

修改 App 的其它部分

现在你已经将 一个 AppConstants 中的值转到 RCValue 了,你可以继续转化其他值!打开 RCValues.swift 将 ValueKey 修改为:

enum ValueKey: String {
  case bigLabelColor
  case appPrimaryColor
  case navBarBackground
  case navTintColor
  case detailTitleColor
  case detailInfoColor
  case subscribeBannerText
  case subscribeBannerButton
  case subscribeVCText
  case subscribeVCButton
  case shouldWeIncludePluto
  case experimentGroup
  case planetImageScaleFactor
}

<div class="se-preview-section-delimiter"></div>

然后,将 loadDefaultValues() 方法修改为:

func loadDefaultValues() {
  let appDefaults: [String: NSObject] = [
    ValueKey.bigLabelColor.rawValue: "#FFFFFF66" as NSObject,
    ValueKey.appPrimaryColor.rawValue: "#FBB03B" as NSObject,
    ValueKey.navBarBackground.rawValue: "#535E66" as NSObject,
    ValueKey.navTintColor.rawValue: "#FBB03B" as NSObject,
    ValueKey.detailTitleColor.rawValue: "#FFFFFF" as NSObject,
    ValueKey.detailInfoColor.rawValue: "#CCCCCC" as NSObject,
    ValueKey.subscribeBannerText.rawValue: "Like Planet Tour?" as NSObject,
    ValueKey.subscribeBannerButton.rawValue: "Get our newsletter!" as NSObject,
    ValueKey.subscribeVCText.rawValue: "Want more astronomy facts? Sign up for our newsletter!" as NSObject,
    ValueKey.subscribeVCButton.rawValue: "Subscribe" as NSObject,
    ValueKey.shouldWeIncludePluto.rawValue: false as NSObject,
    ValueKey.experimentGroup.rawValue: "default" as NSObject,
    ValueKey.planetImageScaleFactor.rawValue: 0.33 as NSObject
  ]
  FIRRemoteConfig.remoteConfig().setDefaults(appDefaults)
}

<div class="se-preview-section-delimiter"></div>

然后,新增 3 个助手方法,以便能够获取 UIColor 之外的其他类型的值:

func bool(forKey key: ValueKey) -> Bool {
  return FIRRemoteConfig.remoteConfig()[key.rawValue].boolValue
}

func string(forKey key: ValueKey) -> String {
  return FIRRemoteConfig.remoteConfig()[key.rawValue].stringValue ?? ""
}

func double(forKey key: ValueKey) -> Double {
  if let numberValue = FIRRemoteConfig.remoteConfig()[key.rawValue].numberValue {
    return numberValue.doubleValue
  } else {
    return 0.0
  }
}

接着将 App 中凡是用到 AppConstants 值的地方全部替换为对应的 RCValue 值。你可以这样:

  1. 打开 ContainerViewController.swift, 将 updateNavigationColors() 改成:

    func updateNavigationColors() {
    navigationController?.navigationBar.tintColor = RCValues.sharedInstance.color(forKey: .navTintColor)
    }
  2. 将 updateBanner() 改成:
    func updateBanner() {
    bannerView.backgroundColor = RCValues.sharedInstance.color(forKey: .appPrimaryColor)
    bannerLabel.text = RCValues.sharedInstance.string(forKey: .subscribeBannerText)
    getNewsletterButton.setTitle(RCValues.sharedInstance.string(forKey: .subscribeBannerButton), for: .normal)
    }
  3. 打开 GetNewsletterViewController.swift, 将 updateText() 改成:
    func updateText() {
    instructionLabel.text = RCValues.sharedInstance.string(forKey: .subscribeVCText)
    submitButton.setTitle(RCValues.sharedInstance.string(forKey: .subscribeVCButton), for: .normal)
    }
  4. 打开 PlanetDetailViewController.swift, 将 updateLabelColors() 中的这一行:
    nextLabel.textColor = AppConstants.detailInfoColor

    修改为:

    nextLabel.textColor = RCValues.sharedInstance.color(forKey: .detailInfoColor)
  5. 将这一行:
    planetNameLabel.textColor = AppConstants.detailTitleColor

    修改为:

    planetNameLabel.textColor = RCValues.sharedInstance.color(forKey: .detailTitleColor)
  6. 打开 PlanetsCollectionViewController.swift, 在customizeNavigationBar() 中将这行:
    navBar.barTintColor =  AppConstants.navBarBackground

    替换为:

    navBar.barTintColor =  RCValues.sharedInstance.color(forKey: .navBarBackground)
  7. 在 collectionView(_:cellForItemAt:) 中, 将这行:
    cell.nameLabel.textColor = AppConstants.bigLabelColor

    替换为:

    cell.nameLabel.textColor = RCValues.sharedInstance.color(forKey: .bigLabelColor)
  8. 打开 SolarSystem.swift, 在 init() 中, 将这一句:
    if AppConstants.shouldWeIncludePluto {
    替换为:
    
    ```swift
    if RCValues.sharedInstance.bool(forKey: .shouldWeIncludePluto) {
  9. 最后, 在 calculatePlanetScales() 中将这句:
    scaleFactors[i] = pow(ratio, AppConstants.planetImageScaleFactor)

    替换为:

    scaleFactors[i] = pow(ratio, RCValues.sharedInstance.double(forKey: .planetImageScaleFactor))

嘘! 好了,改完这么多地方,你可以来确认一下你的 App 是否都改完了。这样,你可以搜索 App 中的所有 “AppConstants” — 你只会找到一个结果,那就是它自己的结构定义。

如果你还不信,你可以把 AppConstants 文件删除。你的 App 依然能够编译,没有任何问题。

现在,你的 App 已经完全支持远程配置了,你可以继续实现 Gary 要求你做的任何其它改变。

打开 Firebase 控制台。在 Remote Config 节点击 Add Parameter。key 输入 navBarBackground ,value 输入 #35AEB1,点击 Add Parameter。重复重样动作将 navTintColor 设为 #FFFFFF。点击 Publish Changes ,将值下发给 App。

你的 Firebase 控制台最终将是这个样子:

App 则是这个样子:

你可以任意调整,试试修改其它值。胡乱改几个文字内容。尝试各种样式……花色……颜色。

但当你做完这一切之后,请回到本教程,这里还有一个严重的问题有待解决!

冥王星回归

事情就坏在丹麦人身上!当世界上大部分人都已经接受冥王星不再是一颗行星的同时,北欧保护冥王星协会,一个由狂热的冥王星迷组成的组织,顽固地坚持冥王星作为一颗行星存在,并应当将它放到 Planet Tour App 中。在你阅读本文的同时,哥本哈根的街道上排满了抗议的人群!怎么办?

重新发布一个 App,则会激怒另一群人……

好吧,使用远程配置,这好像不是太难!你可以将 shouldWeIncludePluto 设为 true。等等,这回改变所有用户的设置,而不仅仅是北欧。怎样才能基于不同的地区下发不同的设置?

答案是 Conditions!

远程配置比起简单的云端字典来说更加智能,那就是根据不同的人群发布不同的设置。你可以利用这个特性允许北欧用户重新迎回它们的冥王星。

首先,打开 Firebase 控制台,在 Remote Config 面板中,点击 Add Parameter 添加一个新参数。

key 输入 shouldWeIncludePluto。

点击 value 栏旁边的 Add value for condition 下拉框。选择 Define New Condition。

在对话框中,给新条件命名为 Pluto Fans。

在下拉框中,选择 Device Region / Country。

在国家列表中,选择 Denmark, Sweden, Norway, Iceland, 和 Finland。

点击 Create Condition。

然后,在 Value for Pluto Fans 栏,输入值 true。在 Default value 栏输入 false。

最后,点击 Add Parameter,再点击 Publish Changes。

运行程序,假设你没有在这些北半球国家,你仍然不能在行星列表中看见冥王星。如果你想体验一下北欧用户,我建议你买一张到哥本哈根的机票,买一部丹麦版的 iPhone,然后打开 App,顺便来一块熏鲑鱼单片三明治。

有一个更经济的做法(同时更少的时差)是,打开设备后模拟器上的设置程序。选择 General > Language & Region > Region > Denmark (或其它北欧国家)

这比飞到哥本哈根要便宜得多了,但同时也少了许多乐趣。

运行程序,这次你可以看见冥王星和别的行星列在一起。呼,避免了一起国际纠纷!

结束语

你可以从这里下载最终项目。但是请注意,你仍然需要在 Firabase 控制台中创建项目,并将你的 GoogleServices-info.plist 文件拖到项目中。

通过远程配置能让你实现许多功能。如果你在开发游戏,如果玩家觉得难度过低或过高,用它来调整游戏玩法是一种好办法。还可以用它来实现“每日提醒”之类的功能。甚至可以用它来实验不同的按钮和标签文本,看看哪种能够让用户体验最好。在你的 App 中试试吧,看看你能改变些什么?

有很多特性还没有来得及展示。通过将值下发给随机的用户组,你可以用远程配置来进行 A/B 测试,或者逐步将新功能推广到其他地区。你还可以将不同的数据集下发给通过 Firebase Analytics 识别出的某个用户组,实现某种定制化效果。如果你想进一步了解,请阅读这个文档

如果你有任何关于 Firebase 远程配置(或者 Planet Tour 颜色方案)的问题或建议,请在下面留言!

时间: 2024-10-08 10:20:58

Firebase 远程配置 iOS 教程的相关文章

Firebase远程更新应用

能打造出色的应用不意味着一定能在商业上取得成功,两者之间还有许多工作要做,绝不能简单发布应用后就宣告“收工”.您需要能迅速根据用户反馈作出调整.测试新功能,以及向用户提供他们最关注的内容. Firebase 远程配置就是为此而生.利用 Firebase 远程配置,您可以在云端更改应用的外观和风格,从而快速响应用户的需求.另外,您还可以利用 Firebase 远程配置向不同用户提供不同内容,从而实现开展实验.逐步推出功能,乃至根据用户在应用中的互动行为向他们提供个性化内容的目的. 更新应用再也不必

Bugtags 远程配置功能介绍

远程配置顾名思义是在不发版的情况下,更改应用的行为和外观.举个例子,如下图:在 Demo Page 中,BUTTON 的颜色不一样,如果想实时修改按钮颜色,测试不同颜色的按钮实际点击情况,该怎么做呢? 其实很简单,只需要三步: Web 云平台创建远程配置 客户端添加代码 开始测试 Web 云平台创建远程配置 进入远程配置,点击创建参数 PS:远程配置与企业版是单独收费的,新用户创建公司后可以免费体验 15 天,若想购买远程配置请联系公司拥有者. 填写参数信息 上图中,红框与绿框区域一会儿我们会在

Xamarin iOS教程之申请付费开发者账号下载证书

Xamarin iOS教程之申请付费开发者账号下载证书 Xamarin iOS使用真机测试应用程序 在讲解iOS Simulator时,已经提到了虽然iOS Simulator可以模仿真实的设备,但是还是有很多的缺陷,如打电话.发送SMS信息.获取位置数据等.如果想要实现iOS Simulator实现不了的功能,就需要使用真机对应用程序进行测试.本节将讲解如何使用真机对应用程序进行测试. Xamarin iOS申请付费开发者账号 使用真机测试,需要申请和下载证书.对于证书的申请和下载必须成为一个

RHEL 6.10系统安装配置图解教程

RHEL 6.10系统安装配置图解教程(rhel-server-6.5) 网址:http://www.jb51.net/os/128752.html截止目前RHEL 6.x最新版本为RHEL 6.10,下面介绍RHEL 6.5的具体安装配置过程,需要的朋友可以参考下 说明: 截止目前RHEL 6.x最新版本为RHEL 6.5,下面介绍RHEL 6.5的具体安装配置过程 服务器相关设置如下: 操作系统:RHEL 6.5 64位 IP地址:192.168.0.54 网关:192.168.0.1 DN

IOS 教程以及基础知识

http://wenku.baidu.com/course/view/1ce3571252d380eb62946d8c http://iphone.apkbus.com/ www.itcast.cn //博客 http://blog.csdn.net/cutesource/article/details/7715593 // ios虚拟开发环境搭建 http://www.cnblogs.com/mjios/ //ios 博客 http://www.9ria.com/subject/summary

Ubuntu 16.04系统下CUDA8.0配置Caffe教程

由于最近安装了Ubuntu 16.04,本文教程的特点是不需要降级gcc的版本,毕竟cuda8.0已经支持gcc5以上(默认不支持,实际支持). 本文是在参考caffe官网教程以及http://www.linuxidc.com/Linux/2015-07/120449.htm结合自己总结经验而来,对此表示感谢. Ubuntu 14.04安装Nvidia CUDA7.5并搭建Python Theano深度学习开发环境http://www.linuxidc.com/Linux/2015-09/123

远程获取iOS设备的屏幕截图

一个远程获取iOS设备屏幕的例子,Client采用TCP连接iOS设备的2115端口,然后读取PNG格式的数据流. +VSRemoteScreen.h +VSRemoteScreen.m 添加到你的iOS项目中,然后在App启动时调用startScreenServer函数. +client.php client示例文件 [1].[代码] RemoteScreen 跳至 [1] [2] ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

webpack安装配置使用教程详解

webpack安装配置使用教程详解 www.111cn.net 更新:2015-09-01 编辑:swteen 来源:转载 本文章来为各位详细的介绍一下关于webpack安装配置使用教程吧,这篇文章对于不喜欢使用 jspm测试的朋友可以参考一下. webpack 入门 目录 1 安装 webpack2 初始化项目3 webpack 配置4 自动刷新5 第三方库6 模块化7 打包.构建8 webpack 模板我最近大量使用的是 jspm,但因为用它搭建的前端开发环境中,写测试代码非常困难,而项目又

Oracle- PL/SQL DEV的远程配置

首先装好Oracle企业版或者客户端精简版.之后要修改你的的tnsnames.ora文件,来增加数据库. 我的ORACLE实例是ORACLE1,放在D:\oracle\ 目录下.我的IP地址是 192.168.1.102 修改如下: 添加: DEMO = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =  192.168.1.102)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERV