转载

在 IBM Bluemix 上构建移动足球比赛比分通知

作为一个没有时间观看太多比赛的热心的足球迷,我想获得一种了解最新比分的直观方式。大多数相关的应用程序似乎都很臃肿和平淡无奇,所以我决定自己构建一个。

在本教程中,我将介绍我如何构建 “Occer” 应用程序并将它部署在 IBM 的 Bluemix 平台上。后端 API 层是使用 Ruby、Sinatra 和 MongoDB 构建的。它使用免费的 football-data.co.uk 数据提要来获取最新结果。该应用程序本身是一个使用 Swift 编写的 iOS 应用程序,使用了 IBM Bluemix Mobile SDK 来处理推送通知。

本教程将介绍如何构建一个 iOS 应用程序来获取英格兰足球超级联赛的最新足球比分,并在有新比分时发送推送通知。

构建您的应用程序需要做的准备工作

  • 在本地安装 Ruby、RubyGems 和 Bundler
  • 在本地安装 MongoDB
  • Xcode 和一个 iOS Developer Program 帐户
  • 一个物理 iOS 设备,用于测试推送通知
  • 一个 IBM Bluemix 帐户
  • cf 命令行实用程序

您可以从 IBM DevOps Services 的 “moccer” 项目中获取下面的说明中提及的代码。

获取代码

第 1 步. 构建后端 API

构建此应用程序的第一步是创建一个后端,该后端将执行以下操作:

  • 从 football-data.co.uk 抓取并解析最新信息提要
  • 如果有新结果,则更新 MongoDB 数据库
  • 提供 API 端点来查找更新和获取给定日期的比分

创建一个新 Sinatra 应用程序

我们首先会创建一个基本的 Sinatra 应用程序。

  1. 为您的项目创建一个目录,向其中添加一个名为 Gemfile 的新文件。将以下代码添加到此文件中。
    source 'https://rubygems.org'     gem 'mongo'     gem 'bson'     gem 'bson_ext'     gem 'sinatra'     gem 'thin'
  2. 在命令提示符下,导航到您的项目目录,运行以下代码来安装依赖项。
    $ bundle install
  3. 创建一个名为 app.rb 的文件并向其中添加以下代码。

    require 'sinatra'      get '/' do       'Hello, world!'     end

    此代码定义一个非常基本的 “Hello, World” Sinatra 应用程序,该应用程序在用户导航到它的根目录时显示此消息。

  4. 在运行该应用程序之前,创建另一个名为 config.ru 的新文件,并添加以下代码。
    require './app'     run Sinatra::Application
  5. 要启动该应用程序,请在提示符下输入以下命令:
    $ rackup -p 4567

您现在应该能够打开您选择的 Web 浏览器,访问 http://localhost:4567 并看到显示了熟悉的 “Hello, World!” 消息。

抓取比分数据并将该数据存储在 MongoDB 中

我们将整个 app.rb 文件替换为将从 football-data.co.uk 文件抓取数据,并将这些数据存储在 MongoDB 数据库中的代码。确保您有一个本地 MongoDB 数据库正在运行,否则该应用程序不会启动。要进行核实,可以在命令行上运行 mongo 。如果看到一条类似 “connect failed” 的消息,则表明 MongoDB 未在本地运行。

  1. 您需要做的第一件事是加载一些 Ruby gem,它们会帮助您抓取数据,解析 CSV 内容并将其存储在数据库中。确保您已完全删除 app.rb 文件中的所有代码,并将其替换为以下代码。
    require 'net/http'     require 'uri'     require 'csv'     require 'mongo'     require 'sinatra'     require 'json'      include Mongo
  2. 接下来,添加两个配置代码块。第一个代码块仅在开发环境中运行(您将在生产环境中连接到一个 Bluemix 托管的 MongoDB 数据库)。第二个代码块将在任何环境中运行。
    configure :development do   host = 'localhost'   port = MongoClient::DEFAULT_PORT   client = MongoClient.new(host, port)    db = client.db('occer-data')   set :coll, db.collection('scores') || db.create_collection('scores') end  configure do   set :csvUrl, "http://www.football-data.co.uk/mmz4281/1415/E0.csv"    CSV::Converters[:date_to_iso] = lambda do |s|     begin       Date.strptime(s, '%d/%m/%y').strftime('%Y-%m-%d')     rescue       s     end   end end 

    在第一个代码块中,您定义了 MongoDB 连接参数、数据库名称和集合(或者如果该集合还不存在,则创建它)。在第二个代码块中,将会定义用于查找足球比赛结果的 URL。请注意, 1415 表示足球赛季 2014/2015。要抓取另一个赛季的数据,可将 1415 替换为相关的值。您还需要定义一个 CSV 转换器,它将获取日期并将该日期格式化为 ISO 数据标准 (YYYY-MM-DD)。

  3. 定义一个基本的 API 端点,它将抓取该 CSV 文件的最新版本,检查其中是否包含新记录,以及更新 MongoDB 数据库中的数据。将此代码添加到紧挨前一个清单的代码后面。

    get '/update' do   count = settings.coll.find.count    uri = URI.parse(settings.csvUrl)   response = Net::HTTP.get_response uri   csv = CSV.new(response.body, { :headers => :first_row, :header_converters => :symbol,         :converters => [:all, :date_to_iso] })    keep = [ :date, :hometeam, :awayteam, :fthg, :ftag, :ftr, :hthg, :htag, :htr, :referee ]   data = csv.to_a.map { |row| row.to_hash.select { |k, v| keep.include?(k) } }     data.delete_if { |v| v[:ftr] == nil }    records = data.length - count    if records > 0     # Push notification!      settings.coll.remove     settings.coll.insert(data)   end          { :success => true, :records => records }.to_json end 

    此代码将获取数据库中比分统计,抓取最新的数据,并进行比较,以查看是否有新比分。我们放置了一个占位符来发送推送通知。稍后将返回这里。为了简便起见,我们删除了数据库中的所有记录,将它们替换为来自 CSV 文件的最新数据。实际上,您可能希望使用增量更新。

  4. 此刻,您可以启动该应用程序,查看您导航到此 API 端点时发生了什么。为此,首先按下 Ctrl + C 重新启动 Rack 服务器并再次发出以下命令:
    $ rackup -p 4567
  5. 在您浏览器中导航到 http://localhost:4567/update。您会看到一个类似这样的响应:

    {"success":true,"records":110}

    请注意,找到的记录数量取决于您阅读本教程的时间,所以绝不应该是 110。

获取比赛日期列表

该 iOS 应用程序的第一个视图是一个比赛日期列表,允许用户下钻到某个比赛日期来查看那天举行的比赛的比分。为了实现此目的,我们需要提供一个返回比赛日期列表的 API。

  1. 将以下代码添加到 app.rb 文件中。

    get '/dates' do       dates = settings.coll.distinct('date').to_a.sort! { |x, y| y <=> x }       dates.to_json     end

    此代码从 Mongo 集合获取不同日期的列表,将它们存储在一个数组中,然后按逆序排序该数组,并以 JSON 格式返回它。

  2. 如果重新加载 Rack 服务器并导航到 http://localhost:4567/dates,您会看到与以下内容类似的输出:

    点击查看代码清单

    关闭 [x]

    ["2014-11-09","2014-11-08","2014-11-03","2014-11-02","2014-11-01","2014-10-27","2014-10-26","2014-10-25","2014-10-20","2014-10-19","2014-10-18","2014-10-05","2014-10-04","2014-09-29","2014-09-28","2014-09-27","2014-09-21","2014-09-20","2014-09-15","2014-09-14","2014-09-13","2014-08-31","2014-08-30","2014-08-25","2014-08-24","2014-08-23","2014-08-18","2014-08-17","2014-08-16"]

    同样地,请记住您可能会看到比我多得多的日期,因为到您阅读本文时举行了更多比赛。

获取给定日期的比分

最后的 API 端点将抓取一个给定比赛日期的比分数组。它接受一个查询参数,该参数告诉该 API 要查找的日期。如果未提供任何参数,则会获取并使用最大的比赛日期。

  1. 将以下代码附加到 app.rb 文件中并保存。

    点击查看代码清单

    关闭 [x]

    get '/' do   date_str = params[:date]   date = Date.strptime(date_str, '%Y-%m-%d') rescue nil      if date.nil?         maxdate = settings.coll.find({}, :fields => ["date"], :limit => 1, :sort => ['date','desc']).to_a     date_str = maxdate.length > 0 ? maxdate[0]["date"] : ''     date = Date.strptime(date_str, '%Y-%m-%d') rescue nil       end    unless date.nil?     # Get scores that match the given date     scores = settings.coll.find({"date" => date_str}).to_a     scores.to_json   else     [].to_json   end end 

    此 API 在 URI 中寻找一个日期查询参数。如果它未找到,则使用限制和排序指令个从数据库获取最新的比赛日期。它然后在 Mongo 集合中查找所有与给定日期匹配的比分,并以 JSON 格式返回。

  2. 同样地,重新启动服务器进程,这次导航到 http://localhost:4567/。您会看到与下面类似的内容:

    点击查看代码清单

    关闭 [x]

    [{"_id":{"$oid": "5464efb0f10cf604dd0000d9"},"date":"2014-11-09","hometeam":"Sunderland","awayteam":"Everton","fthg":1,"ftag":1,"ftr":"D","hthg":0,"htag":0,"htr":"D","referee":"L Mason"},{"_id":{"$oid": "5464efb0f10cf604dd0000da"},"date":"2014-11-09","hometeam":"Swansea","awayteam":"Arsenal","fthg":2,"ftag":1,"ftr":"H","hthg":0,"htag":0,"htr":"D","referee":"P Dowd"},{"_id":{"$oid": "5464efb0f10cf604dd0000db"},"date":"2014-11-09","hometeam":"Tottenham","awayteam":"Stoke","fthg":1,"ftag":2,"ftr":"A","hthg":0,"htag":2,"htr":"A","referee":"M Jones"},{"_id":{"$oid": "5464efb0f10cf604dd0000dc"},"date":"2014-11-09","hometeam":"West Brom","awayteam":"Newcastle","fthg":0,"ftag":2,"ftr":"A","hthg":0,"htag":1,"htr":"A","referee":"C Pawson"}]

    请记住,您看到的数据可能会有所不同,但键值应该是类似的。

后端现在已完成。现在,让我们将后端部署到 Bluemix。

第 2 步. 将后端部署到 Bluemix

要将应用程序推送到 Bluemix,首先需要向项目目录添加一个 manifest.yml 文件,告诉 Bluuemix 您想要部署的应用程序的一些信息。

  1. 创建此文件并向其添加以下内容。
    applications: - name: occer   host: occer   disk: 1024M   path: .   domain: mybluemix.net   mem: 128M   instances: 1 

    备注:您需要将名称和主机值更改为您应用程序独有的值。

  2. 创建清单文件后,现在可以将应用程序推送到 Bluemix 了。在命令提示符下,运行以下命令,使用 cf 实用程序登录到 Bluemix 并将您的代码推送到云。如果尚未安装 cf 实用程序,请 下载它 。运行 cf login 命令后,在提示时,需要输入您的 Bluemix 帐户电子邮件地址和密码。
    $ cf api https://api.ng.bluemix.net     $ cf login     $ cf push
  3. 当您首先推送应用程序时,它不会实际运行,因为不存在 MongoDB 生产配置,所以将出现一些错误。要更正此错误,您需要配置一个新的 MongoDB 服务实例,并将它绑定到应用程序。再次声明,记住将这里的名称更改为惟一的值,否则应用程序无法运行。对 occer 的引用是您在 manifest.yml 文件中创建的应用程序名称。
    $ cf create-service mongodb 100 occer-db     $ cf bind-service occer occer-db
  4. 打开 app.rb 文件,将以下配置代码块添加到您早先创建的开发配置代码块上方。
    configure :production do   # Get the credentials for the MongoDB Bluemix service   env = JSON.parse(ENV["VCAP_SERVICES"])["mongodb-2.4"].first["credentials"]    # Connect to MongoDB and authenticate using environment variables   conn = MongoClient.new(env["hostname"], env["port"])   db = conn.db(env["db"])   db.authenticate(env["username"], env["password"])    # Store the DB connection in a setting for easy retrieval later   set :coll, db.collection('scores') || db.create_collection('scores') end 
  5. 您现在已准备好将该应用程序推送到 Bluemix。
    $ cf push
  6. 导航到您的应用程序 URL,确认您的应用程序在运行,该 URL 应该类似于 http://occer.mybluemix.net。导航到更新 API 端点地址 http://occer.mybluemix.net/update,确保该应用程序已抓取最新结果。您会看到一个与以下内容类似的结果:
    {"success":true,"records":110}

根据您阅读本教程的时间,您获得的数量可能与 110 不同。请注意,如果您已经填充了该数据库,就会看到一个记录数量 0。这是因为未找到新记录。

接下来,您将构建前端 iOS 应用程序来显示这些比分。

第 3 步. 创建 iOS 应用程序

启动 Xcode 并创建一个新的大纲-细节 iOS 应用程序项目。将它命名为 Occer ,将 Organization Name 和 Bundler Identifier 设置为 Demo 。将 Language 设置为 Swift ,将 Devices 设置为 iPhone 。保留 Use Core Data 未选择并按下 Next 。选择一个位置来存储您的项目,然后将创建该项目。

设计用户界面

  1. 在项目导航器面板中,单击 Main.storyboard 文件。
  2. 这将打开一个设计视图,其中显示了 3 个屏幕:导航控制器、大纲视图和细节视图。双击大纲视图中的标题栏编辑它,将标题更改为 Occer 2014/15
  3. 大纲视图将显示一个比赛日期列表,点击一个日期将显示那天的比分列表。要实现此目的,您需要在细节视图和大纲视图中提供一个表格视图。单击 Detail View content goes here 并按下键盘上的 Backspace 键删除该消息。
  4. 单击细节视图中较大的白色区域来选中它,按下 Backspace 删除该白色区域。删除它后,您会看到灰色的文本 Detail View Controller
  5. 从对象库(通常在底部右侧,第三个图标)中找到一个 Table View 对象( 不是 表格视图控制器),并将它拖到细节视图中。
  6. 将一个 Table View Cell 对象拖到表格视图中。细节视图现在将类似于大纲视图。
  7. 选择您刚添加的 Table View Cell 对象。在 Attributes Inspector 中,将 Style 更改为 Basic
  8. 在标识符字段中输入 Cell
  9. 表格视图单元格中将显示一个标签。单击该标签以选择它。在 Attributes Inspector 中,单击中间的按钮将文本居中。

完成这些操作后,故事板应该类似于:

在 IBM Bluemix 上构建移动足球比赛比分通知

点击查看大图

关闭 [x]

在 IBM Bluemix 上构建移动足球比赛比分通知

UI 工作现在已完成,您已准备好将应用程序连接到后端。

构建类来处理后端 API

  1. 创建两个将连接到 Ruby API 的新类。在 project navigator 中,右键单击 Occer 文件夹并选择 New File
  2. 从打开的菜单中,选择 Swift File 并按下 Next
  3. 将该文件命名为 DateAPIController
  4. 将在 Xcode 编辑器中打开一个空 Swift 文件。将内容替换为以下代码。
    import Foundation  protocol DateAPIControllerProtocol {      func didReceiveDates(dates: NSArray)  }  class DateAPIController {      var delegate: DateAPIControllerProtocol?      init() {      }      func getDates() {          let urlPath = "http://occer.mybluemix.net/dates"          let url = NSURL(string: urlPath)          let session = NSURLSession.sharedSession()          let task = session.dataTaskWithURL(url!, completionHandler: {data, response,   error -> Void in              if(error != nil) {                  println(error.localizedDescription)              }              var err: NSError?              var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: nil,  error: &err) as NSArray              self.delegate?.didReceiveDates(jsonResult)              if(error != nil) {                  println("JSON Error /(err!.localizedDescription)")              }          })          task.resume()      }  } 
    此代码定义了一个协议,允许我们将特定于视图的函数放在该类外,在从服务器收到响应时使用委托来更新 UI。 getDates() 方法在 Bluemix 上查找该 API 并以 JSON 格式抓取数据,然后再在协议中调用委托函数 didReceiveDates()

    备注:一定要将 urlPath 常量更改为您自己的 Bluemix 托管的 Ruby 应用程序的 URL。

  5. 以相同的方式创建另一个新的 Swift 文件,但这一次将它命名为 ScoreAPIController。此文件的代码非常相似,但它会查找比分 API。将此文件的内容替换为以下代码。
    import Foundation  protocol ScoreAPIControllerProtocol {      func didReceiveScores(scores: NSArray)  }  class ScoreAPIController {      var delegate: ScoreAPIControllerProtocol?      init() {      }      func getScores(date: NSString) {          let urlPath = "http://occer.mybluemix.net/?date=/(date)"          let url = NSURL(string: urlPath)          let session = NSURLSession.sharedSession()          let task = session.dataTaskWithURL(url!, completionHandler: {data, response,  error -> Void in              if(error != nil) {                  println(error.localizedDescription)              }              var err: NSError?              var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: nil,  error: &err) as NSArray              self.delegate?.didReceiveScores(jsonResult)              if(error != nil) {                  println("JSON Error /(err!.localizedDescription)")              }          })          task.resume()      }  } 

    此文件与 DateAPIController 文件的主要区别在于, getScores() 方法接受一个日期字符串作为参数,而且这个字符串会作为查询字符串参数附加到 API URL。除此之外,它们的工作原理完全相同。再次声明,确保将 urlPath 常量更改为您自己的 Bluemix 托管的 API 应用程序的实际 URL。

将日期 API 类连接到大纲视图控制器

要让所有功能正常运行,需要将 API 类连接到视图控制器。

  1. 在项目导航器面板中,单击 MasterViewController.swift 显示此文件的代码。您现在将对此文件进行一些更改,以便将它连接到 DatesAPIController。
  2. 找到下面这行,该行定义了 MasterViewController 类:
    class MasterViewController: UITableViewController {
  3. 将此行更改为以下代码,以便让它实现 DateAPIControllerProtocol 协议:
    class MasterViewController: UITableViewController, DateAPIControllerProtocol {
  4. 将把对象定义为 NSMutableArray 的行替换为以下代码:
    var objects = NSArray()     var api = DateAPIController()
    您不需要在对象数组中添加或删除内容,所以应将它定义为一个常规 NSArray ,而不是一个 NSMutableArray 。还要初始化 DateAPIController 类。
  5. viewDidLoad() 方法更改为以下形式:

    点击查看代码清单

    关闭 [x]

    override func viewDidLoad() {  super.viewDidLoad()  // Do any additional setup after loading the view, typically from a nib.  refreshControl = UIRefreshControl()  refreshControl!.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)  tableView.addSubview(refreshControl!) } 

    这会向表格视图添加一个 pull-to-refresh 控件子视图。执行此操作时,会调用 refresh() 方法。

  6. viewDidLoad() 方法下,添加以下代码:
    override func viewWillAppear(animated: Bool) {  super.viewWillAppear(animated)  self.refresh(self) } func refresh(sender: AnyObject) {  self.api.delegate = self  self.api.getDates() } func didReceiveDates(dates: NSArray) {  dispatch_async(dispatch_get_main_queue(), {   self.objects = dates   self.tableView.reloadData()   self.refreshControl?.endRefreshing()  }) } 
    只要出现该表格视图, viewWillAppear() 方法就会刷新该表格。视图出现时或使用表格的 pull-to-refresh 控件时,将调用 refresh() 方法。最后, didReceiveDates() 方法是早先定义的 DateAPIControllerProtocol 的实现。它将对象数组设置为 API 调用的结果,重新加载表格视图数据,并告诉 pull-to-refresh 控件停止刷新。
  7. 完全删除 insertNewObject() 方法。此应用程序不需要它。
  8. prepareForSegue() 方法中,找到下面这行:
    let object = objects[indexPath.row] as NSDate
    将此行更改为以下代码:
    let object = objects[indexPath.row] as NSString
  9. 保留接下来几个方法不变,找到下面这个方法定义:

    点击查看代码清单

    关闭 [x]

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  10. 在此定义内,找到下面这行:
    let object = objects[indexPath.row] as NSDate
    将此行更改为以下代码:
    let object = objects[indexPath.row] as NSString
  11. 删除最后两个方法,因为不需要使用它们: canEditRowAtIndexPathcommitEditingStyle 表格视图方法。
  12. 保存文件。

接下来,您将连接 DetailViewController。

将比分 API 连接到细节视图控制器

  1. 您需要向 DetailViewController 添加一点代码,因为目前尚未包含所需的表格视图方法。首先,找到这个类定义:
    class DetailViewController: UIViewController {
  2. 更改此定义,使该类实现 ScoreAPIControllerProtocol 协议:
    class DetailViewController: UITableViewController, ScoreAPIControllerProtocol {
  3. 删除下面这行:
    @IBOutlet weak var detailDescriptionLabel: UILabel!
    将它替换为以下代码:
    var objects = NSArray()     var api = ScoreAPIController()
  4. 更改 detailItem 的定义,使它成为 NSString? 类型,而不是 AnyObject? 类型:
    var detailItem: NSString? {         didSet {             // Update the view             self.configureView()         }     }
  5. configureView() 方法内容替换为对一个名为 refresh 的新方法的调用,我们稍后将创建该方法。
    func configureView() {         self.refresh(self)     }
  6. viewDidLoad() 方法中,将 self.configure() 行替换为以下代码,以实现 pull-to-refresh:

    点击查看代码清单

    关闭 [x]

    refreshControl = UIRefreshControl()     refreshControl!.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)     tableView.addSubview(refreshControl!)
  7. 在此方法下,添加以下代码:
    override func viewWillAppear(animated: Bool) {  super.viewWillAppear(animated)  self.refresh(self) } func refresh(sender: AnyObject) {  if let detail:NSString = self.detailItem {   self.title = detail   self.api.delegate = self   self.api.getScores(detail)  } } func didReceiveScores(scores: NSArray) {  dispatch_async(dispatch_get_main_queue(), {   self.objects = scores   self.tableView.reloadData()   self.refreshControl?.endRefreshing()  }) } 
  8. 保留 didReceiveMemoryWarning() 方法不变,添加以下 3 个方法来完成该类:
    // MARK: - Table View override func numberOfSectionsInTableView(tableView: UITableView) -> Int {  return 1 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {  return objects.count } override func tableView(tableView: UITableView, cellForRowAtIndexPath   indexPath: NSIndexPath) -> UITableViewCell {  let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)   as UITableViewCell  let object = objects[indexPath.row] as NSDictionary  let homeTeam = object["hometeam"] as NSString  let awayTeam = object["awayteam"] as NSString  let homeScore = object["fthg"] as NSInteger  let awayScore = object["ftag"] as NSInteger  cell.textLabel.text = "/(homeTeam) /(homeScore) - /(awayScore) /(awayTeam)"  return cell } 

在 iOS 模拟器中运行该应用程序

  1. 在工具栏中按下 Play 按钮,在 iOS 模拟器中构建并启动该应用程序。除非构建失败,否则您会在 iOS 模拟器中看到一个类似下图的屏幕。 在 IBM Bluemix 上构建移动足球比赛比分通知
  2. 点击一个日期,您会看到一个类似下图的屏幕: 在 IBM Bluemix 上构建移动足球比赛比分通知

提示:如果应用程序成功构建但在启动时崩溃了,应用程序可能无法连接到 Bluemix API。确保您在 API 地址中拥有正确的 URL,而且拥有互联网连接。

在真实设备上运行应用程序

要让应用程序在真实设备上运行,需要执行一些步骤,尤其在您想让推送通知在下一节中生效的时候。

  1. Project Properties 中打开 Background Modes 功能。选择 Remote notifications 选项。 在 IBM Bluemix 上构建移动足球比赛比分通知

    点击查看大图

    关闭 [x]

    在 IBM Bluemix 上构建移动足球比赛比分通知

  2. 要在物理设备上运行该应用程序,需要安装一个配置概要文件。如果还没有这个概要文件,Xcode 会自动为您设置一个。不幸的是,这将使用一个通配符 App ID,这个 ID 无法用于拥有推送通知功能的应用程序。因此,您需要为您的应用程序设置一个显式的 App ID。为此,导航到 Apple Developer Center 并登录到 Member Center。
  3. 单击 Certificates, Identifiers & Profiles 链接。
  4. 单击 Identifiers 并选择右侧的 App ID ,然后按加号按钮来创建一个新 App ID。
  5. 为应用程序输入一段描述(比如 Occer App ID ),并确保选择了 Explicit App ID 。Bundle ID 值应该与您应用程序的 Bundle Identifier 匹配,该标识符可在 Project Properties 中的 General 表格中找到(如果已按照早先的指南进行操作,该值应该类似于 Demo.Occer )。
  6. 在 App Services 列表中,确保选择了 Push Notifications 并按下 Continue
  7. 再次检查选择并按下 Continue 来创建 App ID。
  8. 您现在需要为该 App ID 启用推送通知,生成一个可供 Push Notification 服务使用的客户端 SSL 证书。在 App IDs 中,选择您刚创建的 App ID。请注意,在 Application Services 中, Push Notifications 选项拥有值 Configurable在 IBM Bluemix 上构建移动足球比赛比分通知
  9. 按下 Edit 按钮。
  10. 在 Push Notifications 下,请注意您可以选择为开发和生产环境创建 SSL 证书。按下 “Development SSL Certificate” 下的 Create Certificate 按钮。按照显示的如何创建证书签名请求 (CSR) 的说明操作。单击 Continue
  11. 选择您创建的 CSR 文件并上传它。
  12. 单击 Generate 生成您的证书。
  13. 将 .cer 文件下载到您的计算机,打开它,以便将它添加到您的 Keychain 中。
  14. 在以后使用 Bluemix Push 时,需要上传一个 P12 密钥文件。让我们现在就生成一个密钥文件。从 “My Certificates” 类别中选择 Apple Development IOS Push Services certificate 并展开它,以便显示私钥。
  15. 同时选择证书和私钥,转到 File > Export Items 。确保已选中 Personal Information Exchange (.p12) 文件格式,单击 Save 将证书导出到您的计算机。
  16. 系统会要求您输入密码并进行确认。在将此文件上传到 IBM Bluemix 时,需要使用此密码,所以不要忘记它。
  17. 系统会提示您输入您的登录密钥链密码,然后会创建您的 P12 文件。
  18. 接下来,需要创建一个新的配置概要文件。从 Apple Developer Center 中的左侧菜单,选择 “Provisioning Profiles” 下的 Development 。然后单击加号按钮添加一个新的概要文件。
  19. 在第一个屏幕上,选择 iOS App Development 并按下 Continue
  20. 确保已选择您刚创建的 App ID,再次按下 Continue
  21. 选择该应用程序用于签名的证书(Xcode 应已为您创建此证书。如果尚未创建该证书,可以尝试在物理 iOS 设备上运行您的应用程序。)
  22. 接下来,选择要包含在配置概要文件中的设备。
  23. 最后,为该配置概要文件提供一个名称并单击 Generate 。下载该配置概要文件并在 Finder 中打开它。
  24. 安装配置概要文件后,确保使用了正确的配置概要文件来对您的应用程序签名。在 Xcode 中,转到 Project Properties 页面并导航到 Build Settings 选项卡。确保显示了 所有 部分。找到 “Code Signing” 部分,查找 Provisioning Profile 选项。从此列表中选择您刚刚创建的概要文件。
  25. 您现在应该能够在实际设备上运行该应用程序,而且应该将它配置为接收推送通知,我们将在下一节介绍推送通知。下图显示该应用程序正在 iPhone 6 Plus 上运行。这可能看起来类似于图 2,但请注意运营商名称是真实的运营商,而且显示了网络信号。 在 IBM Bluemix 上构建移动足球比赛比分通知

现在我们向应用程序添加推送通知功能,完成该应用程序。

第 4 步. 实现推送通知

  1. 登录到 IBM Bluemix 仪表板 ( https://www.bluemix.net )。
  2. 导航到您的应用程序并单击 Add a Service
  3. 要添加推送服务,还需要添加 Mobile Application Security 服务,所以首先应该添加该服务。为该服务提供一个名称,比如 occer-mas (应该是惟一名称),然后创建它。
  4. 在系统要求您重新启动应用程序时按下 Cancel
  5. 再次单击 Add a Service ,这一次从 Mobile 类别中选择推送服务。
  6. 为该服务提供一个名称,比如 occer-push
  7. 在系统要求您重新启动应用程序时,按下 OK

在 Bluemix 中配置推送服务

  1. 单击您创建的 occer-push 服务,并在 Configuration 选项卡中,单击 Apple Push Notification Service (APNS) 下的 Edit
  2. 在 Sandbox Configuration 部分,选择您早先在 Keychain Access 中创建的 P12 证书文件,输入您选择的密码。
  3. 单击 Save 。APNS 配置将经过验证。这需要一段时间,完成后您会看到一条绿色的确认消息。 在 IBM Bluemix 上构建移动足球比赛比分通知
  4. 接下来,向 iOS 应用程序添加一些代码,以便允许它接收推送通知。此代码要求用户接受或拒绝接收来自应用程序的推送通知。为此,您需要下载 Bluemix iOS SDK ( https://www.ng.bluemix.net/docs/#starters/mobile/index.html )。
  5. 解压该 zip 文件,复制 IBMBluemix.framework 和 IBMPush.framework 文件夹,然后使用 File > Add Files to Occer 导入到您的 Xcode 项目中。确保 Copy items if needed 已被选中。Bluemix SDK 是使用 Objective-C 编写的,所以开始在 Swift 代码中使用它之前,您需要做一些准备工作。
  6. 向您的项目添加一个新文件。从模板对话框中选择 Header File ,并将该文件命名为 Occer-Header-Bridge.h 。删除该文件的内容,将其替换为以下代码。
    #import <IBMBluemix/IBMBluemix.h>     #import <IBMPush/IBMPush.h>
  7. 现在告诉项目查找此文件作为 Objective-C 桥的头文件。在 Project Properties 中,转到 Build Settings 选项卡,在靠近底部的位置找到一个名为 “Swift Compiler - Code Generation” 部分。编辑选项 Objective-C Bridging Header ,使用值 Occer-Header-Bridge.h

    备注:如果该文件已添加到您应用程序结构中的 Occer 子文件夹中,您可能需要值 Occer/Occer-Header-Bridge.h

  8. 在 Swift 代码中使用 Bluemix SDK 之前,转到 IBM Bluemix 并导航到您的 Occer 应用程序的 Overview 页面。单击 Mobile Options 下拉列表来显示您的应用程序的一些信息,包括路由、App ID 和 App Secret。片刻之后,您需要将这些信息插入到 Xcode 项目中。
  9. 打开 AppDelegate.swift 文件并找到 didFinishLaunchingWithOptions 方法。将此方法替换为以下代码。
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:     [NSObject: AnyObject]?) -> Bool {  // Override point for customization after application launch.  IBMBluemix.initializeWithApplicationId("yourappid", andApplicationSecret: "yourappsecret",     andApplicationRoute: "yourapproute.mybluemix.net")  IBMPush.initializeService()  var type = UIUserNotificationType.Badge | UIUserNotificationType.Alert |     UIUserNotificationType.Sound  var settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes:     type, categories: nil)  application.registerUserNotificationSettings(settings)  application.registerForRemoteNotifications()  return true } 
  10. 确保已将 yourappidyourappsecretyourapproute 值替换为您应用程序的实际值。此代码初始化 IBM SDK 并尝试向应用程序注册远程通知。在用户第一次启动该应用程序时,他会被要求允许发送该应用程序的推送通知。以后不会再收到此提示,但应用程序每次启动时都会尝试注册。如果用户选择不允许发送推送通知,但在以后通过 iOS 设置手动授予访问权,应用程序将在下次启动时应用此更改。

  11. 将以下代码添加到 AppDelegate.swift 文件的末尾处,放在结束 } 的前面:
    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken   deviceToken: NSData) {  var pushService: IBMPush = IBMPush.service()  pushService.registerDevice("occeralias", withConsumerId: "occerconsumerid", withDeviceToken:   deviceToken.description).continueWithBlock({   (task: BFTask!) -> BFTask in   if task.error() != nil {    println(task.error().description)   } else {    println(task.result().description)   }   return task  }) } func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError     error: NSError) {  println("Push Registration Failed. error: /(error.debugDescription)") } 

    如果用户向应用程序授予了向设备发送推送通知的权限,此代码会向 IBM 推送服务注册该设备。 occeraliasoccerconsumerid 值无关紧要。本例中的惟一 ID 是设备令牌。

  12. 在执行这些更改后,再次在物理设备上运行该应用程序。这一次,您将被要求授予接收来自应用程序的推送通知的权限。点击 Allow 。如果意外地单击了 Not Now ,可以随时通过 iOS 设置启用推送通知。请注意,此提醒只显示一次,然后不会再在此设备上显示。 在 IBM Bluemix 上构建移动足球比赛比分通知

从 Bluemix 发送测试推送通知

现在设备已注册了推送通知,您可以向设备发送一个测试通知。

  1. 在 IBM Bluemix 仪表板中,导航到应用程序的推送服务,并转到 Registrations 选项卡。您会在这里看到一个针对您设备的条目。接下来转到 Notification 选项卡。确保 Sandbox 已被选中。在 Message text 字段中,输入 Testing push notifications for Occer ,按下 Next
  2. 在 Choose Recipients 屏幕中,选择 All registered mobile devices ,单击 Send

您看到的通知类型取决于您的设备目前是否锁定。如果设备是锁定的,那么您会看到与下面这个屏幕类似的屏幕。

在 IBM Bluemix 上构建移动足球比赛比分通知

如果您点击该提醒,它会直接启动 Occer 应用程序。

在添加新比分时发送推送通知

从 Bluemix 仪表板发送推送通知不是很有用。我们更改一下 Ruby 后端,以便它会在每次向数据库添加新记录的时候发送通知。

  1. 在 Ruby 项目中打开 app.rb 文件,在 /update 路由中找到下面这行:
    # Push notification!
  2. 在此行下方,添加以下代码:
    app_id = 'yourappid' app_secret = 'yourappsecret' push_url = URI('https://mobile.ng.bluemix.net/push/v1/apps/' + app_id + '/messages') https = Net::HTTP.new(push_url.host, push_url.port) https.use_ssl = true push_body = {  :message => {   :alert => 'New English Premier League soccer scores are available'  }   }.to_json  req = Net::HTTP::Post.new(push_url.path, initheader = {'Content-Type' => 'application/json'}) req['IBM-Application-Secret'] = app_secret req.body = push_body res = https.request(req) 
  3. 保存该文件并使用以下命令将您的应用程序推送到 Bluemix:
    $ cf push

当然,要实际测试此功能的运行情况,需要触发该数据库的一次更新,以便从 football-data.co.uk 服务查找新比分。自您上次更新 Bluemix 数据库中的比分以来,不太可能添加了新比分。幸运的是,您可以在本地强制添加新比分,方法是使用 mongo CLI 客户端登录 MongoDB 并从数据库中删除所有比分。完成这些操作之后,使用以下命令再次启动您的本地应用程序:

$ rackup -p 4567

您现在可以返回到 http://localhost:4567/update 下载更新的比分。这会导致发送推送通知。

每天自动检查更新

最后一步是确保您的应用程序每天检查比分更新。这通常使用 cron 作业来完成。不幸的是,IBM Bluemix 没有 cron 作业的概念。但是,它有一个名为 Workload Scheduler 的服务可用来实现同样的目的。您可以轻松地构建一个应用程序,使用 Workload Scheduler 每天自动向您的 Bluemix 有一些的 /update API 端点发送 HTTP GET 请求。

也可以使用免费的 Web 服务(比如 SetCronJob )来实现同样的目的,无需构建另一个应用程序。

结束语

本教程介绍了大量技术,从 Ruby 和 Sinatra 到 iOS 应用程序和推送通知。作为开发人员,Bluemix 平台使您能够将精力集中在构建优秀的功能上,无需担忧服务器和设备管理。本教程所描述的应用程序只是您可以使用 Bluemix 实现的功能的一个小示例。我很期待看到您提出新的想法。

正文到此结束
Loading...