20200211のiOSに関する記事は5件です。

SAP CP Mobile Services (Cloud Foundry) のPush通知APIを叩いてみた

はじめに

この記事は「SAP S/4HANAで購買発注が登録されるたびiPhoneに「アイーン」とPush通知が来たらぜったいどきどきしちゃう」シリーズの2つ目の記事です。

前回までの実装

前回の記事では、「SAP S/4HANAで伝票登録されたら「あい〜〜ん」Push通知が来たらぜったいどきどきしちゃう」という奇特な業務ユーザからの要望をかなえるべく、SAP Cloud Platform (Cloud Foundry) Mobile Servicesから「アイーン」というPush通知を受け取れるiOSアプリを構築してみました。

このアプリをS/4HANAのプロセスと統合するためには、なにはともあれAPIを活用してこの「アイーン」通知機能を呼び出すことが必要になります。

そこで今回は、Mobile ServicesのPush APIを活用してiPhoneアプリに「アイーン」通知を送ってみようと思います。

今回の目標

今回の記事のゴールは、PostmanからMobile ServicesのAPIを叩いてPush通知リクエストを送り、「アイーン」通知をiPhoneアプリで受け取ることです。
スクリーンショット 2020-02-11 22.46.55.png

前提条件

開発実行環境

  • フロントエンド実行環境: iPhone11 pro iOS 13.3
  • フロントエンド開発環境: macOS Mojave 10.14.6 / Xcode 11.3 ※ Apple Developer Account(有料)を取得していること。
  • バックエンド開発実行環境: SAP S/4HANA 1809
  • クラウド開発実行環境: SAP Cloud Platform (Cloud Foundry trial account) ※ 無料
    • Mobile Services
    • Integration Service

事前手順

前回の記事と同様の手順でPush Notification関連設定を済ませたiOSアプリケーションをMobile Servicesを活用して構築済みであること。

構築手順

それでは構築を始めていきましょう。
前回の記事ですでにだいたい作っているので、実は手順は(答えが分かってさえいれば)大したことありません。

権限設定

前回の記事での権限設定(Role Collection MobileServiceNotificationの割当)をしたと思うのですが、年末に仕様変更があったのかこの権限が出てこなくなっていたので(うーん)、自分でRole Collectionを作って割り当てることにしましょう。

SAP Cloud Platform Cockpitの"Security" > "Role Collection" > "New Role Collection"ボタンより、
名前はなんでもいいのですがMobile_NotificationというRole Collectionを作成しました。

作成したRolle Collectionの名前をクリックします。
スクリーンショット 2020-02-08 16.16.21.png

"Add Role"ボタンを押してRoleを紐づけていきます。
スクリーンショット 2020-02-08 16.18.33.png

場合によっては様々なApplication Identifierが選択可能だと思われますが、Mobile Servicesのアプリケーションインスタンスを作成したときに命名したインスタンスの名称を含んでいるApplication Identifier配下のRoleを紐づけていきます。

スクリーンショット 2020-02-08 16.24.07.png

(今回はとりあえず疎通できるようにしたかったのでApplication Identifier配下のRoleを全部紐づけてしまいましたが、本当はきちんと選択する必要がありますね。)

最後に、"Security" > "Trust Configuration" > "SAP ID Service" にて、
対象のユーザのメールアドレスを入力して"Assign Role Collection"ボタンを押下し、Role Collectionを紐づけます。

スクリーンショット 2020-02-09 8.56.48.png
スクリーンショット 2020-02-09 8.58.17.png

API仕様の確認

Mobile ServicesのPush通知関連APIの種類

SAPのAPIのリファレンスといえばもちろんSAP API Business Hubです。
まずこちらを見るのが王道であると思われるので検索してみると、確かにRemote Push Notification APIと呼ばれるAPIのリファレンスが存在しています。

ところがCloud Foundry環境のMobile Servicesのヘルプドキュメントを見てみると、似て非なるPush API情報(Native Push Notification for Backend)が記載されています。

迷わしいですが、SAP API Business Hubの情報がNeo環境のサービス向けなのかCloud Foundry環境のサービス向けなのか判然としなかったので、Cloud Foundryを前提としたHelpドキュメントに載っているNative Push Notification for Backendの方を参照することにしました(その結果疎通しました)。

APIをぜんぶ動確する元気はなかったので、この辺りのしゃっきりした情報をお持ちの方はぜひ教えてください。。

APIエンドポイント

上記のドキュメントを参照し、下記の比較的シンプルなエンドポイント(通知先のユーザもデバイスも指定しない)を用いることにします。
https://<mobile services host>/restnotification/application/<applicationId>

今回は一人遊びで実装しているので気にしないことにしますが、エンドポイントやパラメータによって通知先のユーザやデバイスを個別に指定することもできるようです。興味のある方はぜひHelp ドキュメントをご確認ください。

APIホスト

Mobile ServicesのAPIのホストが分からなくてずっと泣いていたのですが、結論としては下記のように作ることができるようです。
https://<subaccount name>-<space name>-<mobile app instance name>.cfapps.eu10.hana.ondemand.com

今回でいえばおよそ下記のような感じです。
https://s0099999999trial-dev-mobileaiiiiin.cfapps.eu10.hana.ondemand.com

APIリクエストbody

Native Push Notification for Backendは、その名の通りさまざまなNative通知サービスの仕様に対応できるよう、かなり柔軟に色々なパラメータをbodyに設定することができます。

今回はiOSの通知なので、APNs(Apple Push Notification Service)と呼ばれる通知の仕様に合わせたパラメータを設定してあげると、iOSっぽい通知の情報を設定することができます。

SAPのドキュメントAppleのドキュメントなどを参考にしてbodyの中身を決めていきました(具体的な設定値は次のセクション参照)。

APIの動作確認

それでは上記の情報を元に、下記の設定内容でPostmanからAPIを叩いてみましょう!

設定内容

Method: POST

Request URL:
https://s0099999999trial-dev-mobileaiiiiin.cfapps.eu10.hana.ondemand.com/restnotification/application/mobileaiiiiin

Authorization:

設定値
TYPE Basic
Username SAP Cloud Platformユーザのログインメールアドレス
Password SAP Cloud Platformユーザのログインパスワード

Headers:

設定値
Content-Type application/json

Body:

{
    "alert": "{\"title\": \"あい〜〜〜〜〜ん\",\"body\" : \"ドリフ大爆笑DVDの〇〇〇〇伝票(999999999999)が登録されました!\"}",
    "badge": 1,
    "sound": "sumikko_aiiiin.aiff"
}

※ 通知のSoundをカスタムしたい場合、XCode側での設定が必要ですが、今回の記事では手順の紹介を割愛します。

叩いてみた

"Send"ボタンを押すと……
スクリーンショット 2020-02-09 9.58.54.png

あい〜〜〜〜〜ん
IMG_0354.PNG

次回は・・・

というわけで、Mobile ServicesのPush通知APIをじょうずに使うことができました!

次回以降の記事にて、このAPIをSAP Cloud Platform Integration ServiceのIntegration Flowから呼び出せるようにしていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SAP S/4HANAで購買発注が登録されるたびiPhoneに「アイーン」とPush通知が来たらぜったいどきどきしちゃう(まとめ)

README

この記事は「SAP S/4HANAで購買発注が登録されるたびiPhoneに「アイーン」とPush通知が来たらぜったいどきどきしちゃう」シリーズのまとめ記事です。このシリーズでは、その名の通りSAP S/4HANAでの伝票登録をトリガーにiPhoneにPush通知を送信するための諸々の構築方法について説明しています。

複数に分割して書いた記事へのリンクをこの記事にまとめておくことにします。

INDEX

実装方法をまとめた記事は下記の4つです。
(もしもやってみたい人がいれば)一応上から順番にやっていけばだいたい実装できるようになっていると思います。

  1. とりあえず「アイーン」とpush通知してくれるiOSアプリをSAP CP Mobile Service (Cloud Foundry) を活用して実装してみた

  2. SAP CP Mobile Services (Cloud Foundry) のPush通知APIを叩いてみた

  3. SAP S/4HANAでの伝票登録をIDocで拾ってSAP CP Integration Serviceをトリガーしてみた ※ アップデート予定あり

  4. SAP CP Integration Serviceでメッセージ編集とかHTTPリクエストとかをやってみた

いったい何を実装しているのか

上記の手順で最終的に実現できる構築物の処理の流れはざっくりこんな感じです。

  • SAP S/4HANAで購買発注伝票が登録されると、IDocと呼ばれる機能がそれを拾って、Remote Function CallでSAP CP Integration Service内のIntegration Flowをトリガーする。
  • IDocからのリクエストbodyのなかに購買発注伝票番号が含まれているので、Integration Flow内でこれをうまく編集し、さらにSAP CP Mobile ServiceのPush通知APIを叩く。
  • APIが叩かれると、iPhoneアプリに「あい〜〜ん! 購買発注伝票(9999999999)が登録されました!」と通知が来る。

……と文章で書いてみましたが、たぶん下記の構成図のほうがわかりやすいかなと思います。

スクリーンショット 2020-02-11 21.31.37.png

どうしてこんなことになったのか

SAP S/4HANAからの通知がスマホに来たらぜったいどきどきしちゃうなと思ったからです。

現に、お店でランチを食べているときに誰かがS/4HANA社内環境で伝票登録をしたらしく、わたしのスマホが大音量で「アイーン」と鳴ったときにはかなりどきどきしました(この記事では説明を省略しているのですが、そのときはこのアプリの通知音を「アイーン」音声にカスタムしていました)。

ぜひ甘酸っぱくもほろ苦いSAPライフのためにこのアプリケーションをお役立てください。

関連情報?

この実装をもとに、chillSAP主催のライトニングトークイベント「SAPなんでもLT祭 ~若手編~」でデモを行いました。
その時のスライドはslideshareにアップロードしているので、よろしければ合わせてご覧ください。

スクリーンショット 2020-02-11 21.55.17.png

おしまい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fastlaneのframeitでunexpected token at {....っていうエラーが出る件

Fastlaneを最新版にしてframeitでApp Store用のスクショを作ろうとしたら変なエラーがでた。

スクリーンショット 2020-02-11 18.17.40.png
なんか文字列がjsonにパースできないらしい。吐き出されてた文字列をコピーしてJson validatorにかけると以下の場所がおかしいことがわかる。
スクリーンショット 2020-02-11 18.21.49.png
Fastlaneのほうではちょうど1週間前にissueが作られていた。

frameit is broken for me in release 2.141.0 #15972

回避方法(2/11時点)

fastlaneを2.138.0に戻す。
(順番に各バージョンに戻して動作確認したところ2.139.0からこのエラーが起きる。)

Gemfileを以下の通り修正

source "https://rubygems.org"

gem "fastlane", "2.138.0" # version指定を追加

で、以下のコマンドで指定したバージョンにfastlaneを巻き戻す。

sudo bundle install

したらエラーが出なくなった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftの記号あれこれ

背景

Swiftの公式ドキュメント、ヘルプガイド、ブログのコードサンプルなどによく出てくる実装のなかで、どんな意味があるのかすぐに判断できない記述がありました。
メソッドの引数が顔文字化していたり(例えば、max(:_:_)とか)、変数定義の型の最後にに?!が付いていたりと、あれ?これはどういう意味だったかなというときに自分自身がすぐに思い出すとき用の記事があるといいなぁと思い記事にしました。

対象とする読者

Swift初心者

環境

私がこの記事を書いている際に利用しているのは次の環境です。
Xcode 11.3.1
Swift 5.0

Swiftの記号あれこれ

関数の引数に出現する_ (アンダースコア)

Swiftでは関数の引数にラベルをあらかじめ付けておき、利用者にそれを明確に指定させることができるが、名前をわざわざ指定しなくても明らかにわかる場合は、その仕組みだとコードが読みにくくなってしまいます。
なので、関数呼び出しするときの引数が、名前なしで渡せますよということを明示するもの。

// 引数に名前付きで呼び出してもらう関数の書き方
func add(x: Int, y: Int) -> Int {
    return x + y
}
var r1 = add(x: 3, y: 2)  // 5: OK
var r2 = add(3, 2)        // NG(引数に名前がないのでコンパイルエラー)


// 引数に名前なしで呼び出してもらう関数の書き方
func addByNoLabel(_ x: Int, _ y: Int) -> Int { 
    return x + y
}
var r3 = add(4, 7)        // 11: 引数に名前なしでOK

関数のシグネチャーで、
func(_:)とかfunc(_:_:)とかの表現を見かけたときは、_:のところに、名前なしで引数を入れれば利用できるんだな、ぐらいで見ておけばよいです。

型のうしろに?(はてな)

?
通称:はてなマーク
正式名:クエスチョンマーク

値がオプショナル型であることを示す場合に利用します。オプショナル型は、指定した型の値もしくは値そのものがないnilのいずれかを表す型です。
Optional<Wrapped> (WrappedにはInt,Stringなど実際の型が入る)が入りますが、コードが冗長となるのを避けるためのシュガーシンタックスとして用意されています。

// 正式な構文だと
var age: Optional<Int>

// 簡潔に書くと
var age: Int? = 21

// 利用時には、アンラップが必要(強制アンラップした場合)
var nextAge :Int = age! + 1   // 22: オプショナルの値に!を付けて強制アンラップ

変数等の後ろに?が出現した場合は、この変数は空っぽの状態(nil)もあり得るということと、Optionalを解除(Wrap包み紙から出してあげるイメージ)しないと、元の型としては利用できないという認識を持っておけばよいです。

型のうしろに!(びっくり)

!
通称:びっくりマーク
正式名:エクスクラメーションマーク

こちらもオプショナル型ですが、?とは異なり!で定義した値にアクセスする際には自動的に強制アンラップして元の型にしてくれます。なので「暗黙的にアンラップされたOptional<Wrapped>型」と呼ばれています。
型としては、Optional<Wrapped>型だけど、利用するときには通常のWrapped型として利用可能。
注意点としては、値にアクセスしたときにnilだった場合は、実行時エラーとなってしまいます。

var age: Int! = 21

//アクセス時に自動的に強制アンラップしてくれる
var nextAge: Int = age + 1   // 22

範囲演算子(終了を含まない)

a..<b
aから始まり、終了bを含まない範囲。

var range 1..<4   // CountableRange(1..<4) 

for value in range {
    print(value)
}

実行結果
1
2
3

範囲演算子(終了を含む)

a...b
aから始まり、終了bを含む範囲。

var range 1...4   // CountableClosedRange(1...4) 

for value in range {
    print(value)
}

実行結果
1
2
3
4

さいごに

慣れない言語でのコーディングは不安です。
でも、知ってしまえば、慣れてしまえばそれほど難しくないことは多々あります。
私も今回のこの記事を自分で整理してアウトプットすることで、頭の中を整理できました。
私のようなSwift初心者の方に、この記事が少しでも役立てば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter プラットフォーム固有機能を利用するためのSystemChannels APIについて

はじめに

Flutterからプラットフォーム固有の機能を利用するためのAPIのSystemChannelsについて解説します。

SystemChannelsとは

まず最初に、基本的にこのAPIを使うことは推奨しません。Flutter Framework内部ではこのAPIを多用していますが、以下のように中間レイヤのAPIであるため今後のバージョンアップで変わる可能性が高いです。

SystemChannelsのソースコードを見ると、中身はMethodChannel, EventChannel等を利用していることが分かると思います。MethodChannelやEventChannel等についてはこちらにまとめていますので参考にしてください。

スクリーンショット 2020-02-11 13.16.33.png

SystemChannelsの種類

用途毎にいくつかの種類が用意されています。一覧を以下に示します。

種類 機能 上位のFlutter Service
lifecycle ライフサイクル widget
navigation ナビゲーション widget, system_chrome
system 不明…。ここで利用されているが、実質何もしていない widget
accessibility アクセシビリティ (テキスト読み上げなど) PlatformViews, Semantics
platform システム設定 (画面回転, 終了など) SemanticsService,RouteNotificationMessages etc.
platform_views プラットフォーム固有のビュー操作 AndroidView, UiKitView
skia グラフィックス -
keyEvent キー入力 RawKeyEvent
textInput テキスト入力 TextInput, AndroidView, UiKitView

サンプルコード

lifecycle

ライフサイクルリスナー
SystemChannels.lifecycle.setMessageHandler((message){
  print('<SystemChannels.lifecycle> $message');
  /*
  AppLifecycleState.paused
  AppLifecycleState.inactive
  AppLifecycleState.resumed
  AppLifecycleState.suspending
  AppLifecycleState.detached
   */
  return Future<String>.value();
});

navigation

ナビゲーション操作リスナー
SystemChannels.navigation.setMethodCallHandler((call) {
  print('<SystemChannels.navigation> ${call.method} (${call.arguments})');
  /*
   popRoute
   pushRoute
   */
  return Future<dynamic>.value();
});

system

実質何にも使えず、使われておらず、無視して良さそうです。

とりあえずコールバックだけ設定
SystemChannels.system.setMessageHandler((message) {
  print('<SystemChannels.system> $message');
  return Future<dynamic>.value();
});

accessibility

テキスト読み上げサンプル
SemanticsService.announce('Hello world', TextDirection.ltr)

のFlutter Framework内部実装が以下です。

参照

Dart
final AnnounceSemanticsEvent event = AnnounceSemanticsEvent('Hello world', TextDirection.ltr);
SystemChannels.accessibility.send(event.toMap());

platform

アプリ終了
SystemNavigator.pop()

のFlutter Framework内部実装が以下です。

アプリ終了
SystemChannels.platform.invokeMethod('SystemNavigator.pop');

platform_views

flutter_webで利用しているので参考にしてください。

こんな感じでビューを作成していく
final Map<String, dynamic> args = <String, dynamic>{
  'id': 1,
  'viewType': 'Create WebView',
};
SystemChannels.platform_views.invokeMethod('create', args);

skia

Skiaキャッシュサイズ設定。他に機能がなく、今はこれしか出来ない…
const maxBytes = 4 * 1024 * 1024;
SystemChannels.skia.invokeMethod('setResourceCacheMaxBytes', maxBytes);

keyEvent

キー入力リスナー
SystemChannels.keyEvent.setMessageHandler((message) {
  print('<SystemChannels.keyEvent> $message');
  return Future<dynamic>.value();
});

textInput

キーボードの表示ON/OFF
SystemChannels.textInput.invokeMethod('TextInput.show');
SystemChannels.textInput.invokeMethod('TextInput.hide');
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む