Speaker tips: Summary slide to drive concept home: Use comparison with enumerable interfaces here! Instead of begging for an enumerator, you now give the source an observer… Data flow is reversed: A value of type T is given to you… …reflected as input and hence contravariance in C# 4.0 Implication: Getting stuck in enumerators means getting too little… …which is dual to getting too much or getting flooded for observers!
Can compare to subscribing to a news paper When calling subscribe: You give the address of your letter box (the observer) You get a way to undo the subscription in the future (the IDisposable is like a letter to cancel the subscription) Asynchronous nature! You’re not blocked till the next news paper arrives. The observer is the letter box: OnNext happens when the postman drops the newsletter in it OnError happens when someone bombs your letter box or the postman drops dead OnCompleted happens when the newspaper publisher goes bankrupt
Transcript
1.
Parseの世界
@maruyama097
丸山 不二夫
2.
“I think the biggest mistake that we made,
as a company, is betting too much on HTML5
as opposed to native…”
Mark Zuckerburg 2012年9月
3.
“Facebook does not hate the Web.”
Tom Occhino f8 2015年4月
4.
Agenda
Part I
モバイル・アプリをめぐる動向
Part II
Parseの世界
Part III
Web技術の新しい展開
5.
モバイル・アプリをめぐる動向
このところ、モバイル・アプリの開発スタイルをめぐっ
て、注目すべき動きが連続している。アプリ開発のス
タイルの大きな変革期に差し掛かっていると考えてい
いと思う。
Part I
6.
Part I
モバイルアプリをめぐる動向
モバイル・アプリをめぐる新しい動き
新しいモバイル・アプリの諸特徴とその背景
FacebookとWebテクノロジー
48.
FacebookのHTML5からの離脱
TechCrunch誌とのインタビュー 2012年9月11日
I think the
biggest mistake
that we made,
as a company,
is betting too
much on HTML5
as opposed to
native…
because it just
wasn’t there.
会社として、我々が犯した最大の誤りは、ネーティブに対して、
HTML5に、あまりに賭けすぎたことだと思う。
49.
Facebook does not hate the Web.
But we're realists. We just can't use the Web right now
to build the types of user experiences that we want
Tom Occhino f8 2015 React Native
55.
Parse Old Web Apps
Data Object
MVC
HTML/HTTP
get/Fetch Save
56.
Parse.Object
クラスとインスタンスの生成
Parse JavaScript
// Parse.objectのサブクラスを生成する簡単なシンタックス
var GameScore = Parse.Object.extend("GameScore");
// このクラスの新しいインスタンスを生成する。
var gameScore = new GameScore();
// 先の方法の代わりに、典型的なBackboneのシンタックスを利用することもできる
var Achievement = Parse.Object.extend({
className: "Achievement"
});
57.
A complex subclass of
Parse.Object
// Parse.objectのサブクラスを生成する少し複雑ななシンタックス
var Monster = Parse.Object.extend(“Monster”, // 第一引数 クラス名
{ // 第二引数 インスタンスのメソッド達
hasSuperHumanStrength: function () {
return this.get("strength") > 18;
},
// インスタンスのプロパティの初期値は、initialize で定義する
initialize: function (attrs, options) {
this.sound = "Rawr"
}
},
{ // 第三引数 クラスのメソッド達
spawn: function(strength) {
var monster = new Monster();
monster.set("strength", strength);
return monster;
}
});
var monster = Monster.spawn(200);
alert(monster.get(‘strength’)); // spawnでsetされた 200を表示
alert(monster.sound); // インスタンスの soundの初期値 Rawr を表示.
58.
Data Objectとしての Parse.Object
var number = 42;
var string = "the number is " + number;
var date = new Date();
var array = [string, number];
var object = { number: number, string: string };
var BigObject = Parse.Object.extend("BigObject");
var bigObject = new BigObject();
bigObject.set("myNumber", number);
bigObject.set("myString", string);
bigObject.set("myDate", date);
bigObject.set("myArray", array);
bigObject.set("myObject", object);
bigObject.set("myNull", null);
bigObject.save();
59.
Parse.Object
クラスとインスタンスの生成
Parse Android Java
ParseObject gameScore =
new ParseObject("GameScore");
Parse iOS
PFObject *gameScore =
[PFObject objectWithClassName:@"GameScore"];
Parse .NET
ParseObject gameScore =
new ParseObject("GameScore");
Parse iOS Swift
var gameScore =
PFObject(className:"GameScore")
60.
Parse.Object
オブジェクトの保存 save
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();
gameScore.set("score", 1337);
gameScore.set("playerName", "Sean Plott");
gameScore.set("cheatMode", false);
gameScore.save( null, {
success: function(gameScore) {
// オブジェクトの保存に成功した後で、実行されるべき処理
alert('New object created with objectId: ' + gameScore.id);
},
error: function(gameScore, error) {
// オブジェクトの保存に失敗した後で、実行されるべき処理
// error は、 Parse.Errorで、 error code と messageを持っている
alert('Failed to create new object, with error code: ' + error.message);
}
});
61.
フィールドを、
saveの第一引数で、直接に設定
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();
gameScore. save({
score: 1337,
playerName: "Sean Plott",
cheatMode: false
}, {
success: function(gameScore) {
// The object was saved successfully.
},
error: function(gameScore, error) {
// The save failed.
// error is a Parse.Error with an error code and message.
}
});
70.
Relational Data の表現
Parse Objectでは、Relationalなデータの表現が
可能である。あとで見る Parse Queryを使って、デ
ータの検索が可能となる。
71.
一対一、一対多の関係の表現
// 型を宣言する
var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");
// postを生成する
var myPost = new Post();
myPost.set("title", "I'm Hungry");
myPost.set("content", "Where should we go for lunch?");
// commentを生成する
var myComment = new Comment();
myComment.set("content", "Let's do Sushirrito.");
// postをcommentの parentの値として設定する
myComment.set("parent", myPost);
// myPost とmyCommentの両方が保存される
myComment.save();
MyPost
MyComment
parent
MyPost
MyComment
parent
72.
内部的には、Parseのフレームワークは、整合性を維持するため
に、参照されたオブジェクトを一箇所のみに格納する。オブジェク
トIDを使った場合のみ、次のように、オブジェクトのリンクは可能
である。
var post = new Post();
post.id = "1zEcyElZ80";
myComment.set("parent", post);
myComment.put(“parent”,
ParseObject.createWithoutData("Post", "1zEcyElZ80"));
myComment[“parent”] =
ParseObject.CreateWithoutData("Post", "1zEcyElZ80");
myComment[“parent”] =
PFObject(withoutDataWithClassName:“Post”,objectId:"1zEcyElZ80")
73.
var post = fetchedComment. get("parent");
post.fetch({
success: function(post) {
var title = post.get("title");
}
});
関連付けられたオブジェクトの取得
オブジェクトを取り出しても、デフォールトでは、
Parse.Objectに関連付けられたオブジェクトは取り出せ
ない。これらのオブジェクトの値は、次のようにしないと取
り出せない。 post
fetchedComment
post
fetchedComment
get(“parent”)
74.
var post = myComment[“parent”] as PFObject
post.fetchIfNeededInBackgroundWithBlock {
(post: PFObject?, error: NSError?) -> Void in
let title = post?[“title”] as? NSString
// do something with your title variable
}
ParseObject post =
fetchedComment.Get<ParseObject>(“parent”);
await post.FetchIfNeededAsync();
fetchedComment.getParseObject(“post”)
.fetchIfNeededInBackground(
new GetCallback<ParseObject>(){
public void done(ParseObject post, ParseException e) {
String title = post.getString(“title”);
// Do something with your new title variable
}
});
75.
多対多の関係の表現 relation
Many-to-manyの関係は、Parse.Relation を使って
モデル化される。このモデルは、一つのキーに、
Parse.Objectの配列を格納するのに似ている。ただし、全
ての関係のオブジェクトを一回で取り出す必要がないことを
除いては。
加えて、このことで Parse.Relation は、 Parse.Object
の配列を利用するアプローチと比較して、より多くのオブジ
ェクトにスケールすることが可能になる。
var user = Parse.User.current();
var relation = user.relation("likes");
relation.add(post);
user.save();
1
2
User
Post
likes
1 3
2
76.
ParseUser user = ParseUser.getCurrentUser();
ParseRelation<ParseObject> relation = user.getRelation("likes");
relation.add(post);
user.saveInBackground();
var user = ParseUser.CurrentUser;
var relation = user.GetRelation<ParseObject>(“likes”);
relation.Add(post);
await user.SaveAsync();
var user = PFUser.currentUser()
var relation = user.relationForKey(“likes”)
relation.addObject(post)
user.saveInBackground()
var user = Parse.User.current();
var relation = user.relation("likes");
relation.add(post);
user.save();
78.
relation.getQuery().findInBackground(
new FindCallback<ParseObject>() {
void done(List<ParseObject> results, ParseException e) {
if (e != null) {
// There was an error
} else {
// results have all the Posts the current user liked.
}
}});
IEnumerable<ParseObject> relatedObjects =
await relation.Query.FindAsync();
relation.query().findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if let error = error {
// There was an error
} else {
// objects has all the Posts the current user liked.
}}
90.
// wins < 50という条件で絞る
query = from gameScore in query
where gameScore.Get<int>("wins") < 50
select gameScore;
// wins <= 50という条件で絞る
query = from gameScore in query
where gameScore.Get<int>("wins") <= 50
select gameScore;
// wins > 50という条件で絞る
query = from gameScore in query
where gameScore.Get<int>("wins") > 50
select gameScore;
// wins >= 50という条件で絞る
query = from gameScore in query
where gameScore.Get<int>("wins") >= 50
select gameScore;
.NET ParseでのLINQの利用
91.
var Team = Parse.Object.extend("Team");
var teamQuery = new Parse.Query(Team);
teamQuery.greaterThan("winPct", 0.5);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesKeyInQuery("hometown", "city", teamQuery);
userQuery.find({
success: function(results) {
// ホームタウンで、勝ち越しているチームの検索結果
}
});
ParseQuery<ParseObject> teamQuery = ParseQuery.getQuery(“Team”);
teamQuery.whereGreaterThan(“winPct”, 0.5);
ParseQuery<ParseUser> userQuery = ParseUser.getQuery();
userQuery.whereMatchesKeyInQuery(“hometown”, “city”, teamQuery);
userQuery.findInBackground(
new FindCallback<ParseUser>() {
void done(List<ParseUser> results, ParseException e) {
// results has the list of users with a hometown team with
// a winning record
}
});
ホームタウンで、勝ち越しているユーザーの検索
92.
var teamQuery =
from team in ParseObject.GetQuery("Team")
where team.Get<double>("winPct") > 0.5
select team;
var userQuery =
from user in ParseUser.Query
join team in teamQuery on user["hometown"] equals team["city"]
select user;
IEnumerable<ParseUser> results = await userQuery.FindAsync();
// results will contain users with a hometown team with a winning record
var teamQuery = PFQuery(className:"Team")
teamQuery.whereKey("winPct", greaterThan:0.5)
var userQuery = PFUser.query()
userQuery!.whereKey("hometown", matchesKey:"city",
inQuery:teamQuery)
userQuery!.findObjectsInBackgroundWithBlock {
(results: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// results will contain users with a hometown team with a winning record
}
}
93.
var losingUserQuery = new Parse.Query(Parse.User);
losingUserQuery.doesNotMatchKeyInQuery(
"hometown", "city", teamQuery);
losingUserQuery.find({
success: function(results) {
// results has the list of users with a hometown team with
// a losing record
}
});
ParseQuery<ParseUser> losingUserQuery = ParseUser.getQuery();
losingUserQuery.whereDoesNotMatchKeyInQuery(
"hometown", "city", teamQuery);
losingUserQuery.findInBackground(
new FindCallback<ParseUser>() {
void done(List<ParseUser> results, ParseException e) {
// results has the list of users with a hometown team with
// a losing record
}
});
ホームタウンで、負け越しているユーザーの検索
95.
Relationalな検索
// Parse.Object myPost は、すでに作られているとしよう
var query = new Parse.Query(Comment);
query.equalTo("post", myPost);
query.find({
success: function(comments) {
// comments は、myPostに対するコメントである
}
});
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.whereEqualTo("post", myPost);
query.findInBackground(
new FindCallback<ParseObject>() {
public void done(List<ParseObject> commentList, ParseException e) {
// commentList now has the comments for myPost
}
});
MyPost
MyComment
“post”
96.
// Assume ParseObject myPost was previously created.
var query = from comment in ParseObject.GetQuery("Comment")
where comment["post"] == myPost
select comment;
var comments = await query.FindAsync();
// comments now contains the comments for myPost
PFQuery *query = [PFQuery queryWithClassName:@"Comment"];
[query whereKey:@"post" equalTo:myPost];
[query findObjectsInBackgroundWithBlock:
^(NSArray *comments, NSError *error) {
// comments now contains the comments for myPost
}];
97.
画像付きのコメントの検索
var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");
var innerQuery = new Parse.Query(Post);
innerQuery.exists("image");
var query = new Parse.Query(Comment);
query.matchesQuery("post", innerQuery);
query.find({
success: function(comments) {
// postに対するコメントで、画像付きのコメントの検索結果
}
});
ParseQuery<ParseObject> innerQuery = ParseQuery.getQuery("Post");
innerQuery.whereExists("image");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.whereMatchesQuery("post", innerQuery);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> commentList, ParseException e) {
// comments now contains the comments for posts with images.
}});
98.
var imagePosts = from post in ParseObject.GetQuery("Post")
where post.ContainsKey("image")
select post;
var query = from comment in ParseObject.GetQuery("Comment")
join post in imagePosts on comment["post"] equals post
select comment;
var comments = await query.FindAsync();
// comments now contains the comments for posts with images
PFQuery *innerQuery = [PFQuery queryWithClassName:@"Post"];
[innerQuery whereKeyExists:@"image"];
PFQuery *query = [PFQuery queryWithClassName:@"Comment"];
[query whereKey:@"post" matchesQuery:innerQuery];
[query findObjectsInBackgroundWithBlock:
^(NSArray *comments, NSError *error) {
// comments now contains the comments for posts with images
}];
99.
画像なしのコメントの検索
var Post = Parse.Object.extend("Post");
var Comment = Parse.Object.extend("Comment");
var innerQuery = new Parse.Query(Post);
innerQuery.exists("image");
var query = new Parse.Query(Comment);
query.doesNotMatchQuery("post", innerQuery);
query.find({
success: function(comments) {
// postに対するコメントで、画像なしのコメントの検索結果
}
});
var post = new Post();
post.id = "1zEcyElZ80";
query.equalTo("post", post);
100.
最新のコメント10個の検索
var query = new Parse.Query(Comment);
// Retrieve the most recent ones
query.descending("createdAt");
// Only retrieve the last ten
query.limit(10);
// Include the post data with each comment
query.include("post");
query.find({
success: function(comments) {
// Comments now contains the last ten comments, and the "post" field
// has been populated. For example:
for (var i = 0; i < comments.length; i++) {
// This does not require a network access.
var post = comments[i].get("post");
}
}
});
101.
オブジェクトを数える count
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Sean Plott");
query.count({
success: function(count) {
// The count request succeeded. Show the count
alert("Sean has played " + count + " games");
},
error: function(error) {
// The request failed
}
});
102.
Queryの組み合わせ or
var lotsOfWins = new Parse.Query("Player");
lotsOfWins.greaterThan("wins", 150);
var fewWins = new Parse.Query("Player");
fewWins.lessThan("wins", 5);
var mainQuery = Parse.Query.or(lotsOfWins, fewWins);
mainQuery.find({
success: function(results) {
// たくさん勝っているひと、「あるいは」、ほとんど勝ってない人
},
error: function(error) {
// There was an error.
}
});
103.
Queryの組み合わせ
ParseQuery<ParseObject> lotsOfWins = ParseQuery.getQuery("Player");
lotsOfWins.whereGreaterThan(150);
ParseQuery<ParseObject> fewWins = ParseQuery.getQuery("Player");
fewWins.whereLessThan(5);
List<ParseQuery<ParseObject>> queries =
new ArrayList<ParseQuery<ParseObject>>();
queries.add(lotsOfWins);
queries.add(fewWins);
ParseQuery<ParseObject> mainQuery = ParseQuery.or(queries);
mainQuery.findInBackground(
new FindCallback<ParseObject>() {
public void done(List<ParseObject> results, ParseException e) {
// results has the list of players that win a lot or haven't won much.
}
});
104.
Compound Queries
var lotsOfWins = from player in ParseObject.GetQuery("Player")
where player.Get<int>("wins") > 150
select player;
var fewWins = from player in ParseObject.GetQuery("Player")
where player.Get<int>("wins") < 5
select player;
ParseQuery<ParseObject> query = lotsOfWins.Or(fewWins);
var results = await query.FindAsync();
// results contains players with lots of wins or only a few wins.
105.
Compound Queries
var lotsOfWins = PFQuery(className:"Player")
lotsOfWins.whereKey("wins", greaterThan:150)
var fewWins = PFQuery(className:"Player")
fewWins.whereKey("wins", lessThan:5)
var query = PFQuery.orQueryWithSubqueries([lotsOfWins, fewWins])
query.findObjectsInBackgroundWithBlock {
(results: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// results contains players with lots of wins or only a few wins.
}
}
107.
Promiseを生成する
var successful = new Parse.Promise();
successful.resolve(“The good result.”); // 結果は成功である
var failed = new Parse.Promise();
failed.reject(“An error message.”); // 結果は失敗である
// Promiseの生成時に、その結果が分かっているのなら
// as (resolved) あるいは error (rejected)メソッドを利用できる。
var successful = Parse.Promise.as("The good result.");
var failed = Parse.Promise.error("An error message.");
108.
非同期のメソッドを作る
var delay = function(millis) {
var promise = new Parse.Promise();
setTimeout(function() {
promise.resolve();
}, millis);
return promise;
};
delay(100).then(function() {
// 100m秒後に実行される
});
109.
then メソッド
全てのPromiseは、then という名前のメソッドを持つ。
then は、2つのcallback を持つ。
最初の callback は、Promise が成功した時に呼ばれ、
二つ目の callback は、Promise が失敗した時に呼ば
れる。
obj.save().then(function(obj) {
// the object was saved successfully.
}, function(error) {
// the save failed.
});
124.
Sending Pushes to Channels
LinkedList<String> channels = new LinkedList<String>();
channels.add("Giants");
channels.add("Mets");
ParsePush push = new ParsePush();
push.setChannels(channels);
// Notice we use setChannels not setChannel
push.setMessage("The Giants won against the Mets 2-3.");
push.sendInBackground();
// Send a notification to all devices subscribed to the "Giants" channel.
var push = new ParsePush();
push.Channels = new List<string> {"Giants"};
push.Alert = "The Giants just scored!";
await push.SendAsync();
let channels = [ "Giants", "Mets" ]
let push = PFPush()
// Be sure to use the plural 'setChannels'.
push.setChannels(channels)
push.setMessage("The Giants won against the Mets 2-3.")
push.sendPushInBackground()
126.
Queryの結果にPushを送る
var query = new Parse.Query(Parse.Installation);
query.equalTo('injuryReports', true); // injuryReportsが真のもの
Parse.Push.send({
where: query, // 送出先
data: {
alert: "Willie Hayes injured by own pop fly."
}
}, {
success: function() {
// Push は成功した
},
error: function(error) {
// Handle error
}
}); 負傷者情報を配信する
127.
QueryをChannelに使う
var query = new Parse.Query(Parse.Installation);
query.equalTo(‘channels’, ‘Giants’); // Set our channel
query.equalTo('scores', true);
Parse.Push.send({
where: query, // channelがGiantsで、scoreが真のものへ
data: {
alert: "Giants scored against the A's! It's now 2-2."
}
}, {
success: function() { // Push was successful },
error: function(error) { // Handle error }
});
Giantsチャンネルに得点を配信する
128.
// Create our Installation query
ParseQuery pushQuery = ParseInstallation.getQuery();
// Set the channel
pushQuery.whereEqualTo("channels", "Giants");
pushQuery.whereEqualTo("scores", true);
// Send push notification to queryParse
Push push = new ParsePush();
push.setQuery(pushQuery);
push.setMessage("Giants scored against the A's! It's now 2-2.");
push.sendInBackground();
var push = new Parse.Push();
push.Query = from installation in ParseInstallation.Query
where installation.Get<bool>("scores") == true
select installation;
push.Channels = new List<string> { "Giants" };
push.Alert = "Giants scored against the A's! It's now 2-2.";
await push.SendAsync();
129.
// Create our Installation query
let pushQuery = PFInstallation.query()
// Set channel
pushQuery.whereKey("channels", equalTo: "Giants")
pushQuery.whereKey("scores", equalTo: true)
// Send push notification to query
let push = PFPush()
// Set our Installation query
push.setQuery(pushQuery)
push.setMessage("Giants scored against the A's! It's now 2-2.")
push.sendPushInBackground()
130.
// ユーザーがその場所に近いか調べる
var userQuery = new Parse.Query(Parse.User);
userQuery.withinMiles("location", stadiumLocation, 1.0);
// そのユーザーのPushの送り先を調べる
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery('user', userQuery);
// Send push notification to query
Parse.Push.send({
where: pushQuery,
data: {
alert: "Free hotdogs at the Parse concession stand!"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
スタジアムに近いところにいるユーザーに通知を送る
131.
// Find users near a given location
ParseQuery userQuery = ParseUser.getQuery();
userQuery.whereWithinMiles("location", stadiumLocation, 1.0)
// Find devices associated with these users
ParseQuery pushQuery = ParseInstallation.getQuery();
pushQuery.whereMatchesQuery("user", userQuery);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery);
// Set our Installation query
push.setMessage("Free hotdogs at the Parse concession stand!");
push.sendInBackground();
// Find users in the Seattle metro area
var userQuery = ParseUser.Query.WhereWithinDistance(
"location",
marinersStadium,
ParseGeoDistance.FromMiles(1));
var push= new ParsePush();
push.Query = from installation in ParseInstallation.Query
join user in userQuery on installation["user"] equals user
select installation;
push.Alert = "Mariners lost? Free conciliatory hotdogs at the Parse
concession stand!";
await push.SendAsync();
132.
// Find users near a given location
let userQuery = PFUser.query()
userQuery.whereKey("location", nearGeoPoint: stadiumLocation,
withinMiles: 1)
// Find devices associated with these users
let pushQuery = PFInstallation.query()
pushQuery.whereKey("user", matchesQuery: userQuery)
// Send push notification to querylet push = PFPush()
push.setQuery(pushQuery)
// Set our Installation query
push.setMessage("Free hotdogs at the Parse concession stand!")
push.sendPushInBackground()
136.
Parse.Push.send({
channels: [ "Mets" ],
data: {
alert: "The Mets scored! The game is now tied 1-1.",
badge: "Increment",
sound: "cheering.caf",
title: "Mets Score!"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
JSONObject data = new JSONObject("{
¥“alert¥”: ¥“The Mets scored!¥”,
¥“badge¥”: ¥“Increment¥”, ¥"sound¥": ¥"cheering.caf¥"}");
ParsePush push = new ParsePush();
push.setChannel("Mets");
push.setData(data);
push.sendPushInBackground();
Metsが得点した時に、
Metsチャンネルに通知
する。音付きで。
137.
let data = [
"alert" : "The Mets scored! The game is now tied 1-1!",
"badge" : "Increment",
"sounds" : "cheering.caf"]
let push = PFPush()
push.setChannels(["Mets"])
push.setData(data)
push.sendPushInBackground()
var push = new ParsePush();
push.Channels = new List<string> {"Mets"};
push.Data = new Dictionary<string, object> {
{"title", "Score Alert"}
{"alert", "The Mets scored! The game is now tied 1-1!"},
};
await push.SendAsync();
138.
var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', 'Indians');
query.equalTo('injuryReports', true);
Parse.Push.send({
where: query,
data: {
action: "com.example.UPDATE_STATUS"
alert: "Ricky Vaughn was injured in last night's game!",
name: "Vaughn",
newsItem: "Man bites dog"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
Indians チャンネルに
負傷者情報を流す。
アクション付きで。
139.
JSONObject data = new JSONObject("{
¥“name¥”: ¥“Vaughn¥”,
¥"newsItem¥": ¥"Man bites dog¥"}”
));
ParsePush push = new ParsePush();
push.setQuery(injuryReportsQuery);
push.setChannel("Indians");
push.setData(data);
push.sendPushInBackground();
let data = [
"alert" : "Ricky Vaughn was injured in last night's game!",
"name" : "Vaughn",
"newsItem" : "Man bites dog”
]
let push = PFPush()
push.setQuery(injuryReportsdata)
push.setChannel("Indians")
push.setData(data)
push.sendPushInBackground()
141.
Parse.Push.send({
where: everyoneQuery,
expiration_time: new Date(2015, 5, 10)
data: {
alert: "Season tickets on sale until May 10, 2015"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
ParsePush push = new ParsePush();
push.setExpirationTime(1430762356);
push.setQuery(everyoneQuery);
push.setMessage("Season tickets on sale until May 4th");
push.sendPushInBackground();
指定の日付5/15以降は
通知を受け取れないように
設定する。
142.
var push = new ParsePush();
push.Expiration = new DateTime(2015, 5, 4);
push.Alert = "Season tickets on sale until May 4th";
await push.SendAsync();
// Create date object for tomorrow
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:2015];
[comps setMonth:5];
[comps setDay:4];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
// Send push notification with expiration date
PFPush *push = [[PFPush alloc] init];
[push expireAtDate:date];
[push setQuery:everyoneQuery];
[push setMessage:@"Season tickets on sale until May 4th"];
[push sendPushInBackground];
143.
プラットフォームでターゲットする
// Notification for Android users
var queryAndroid = new Parse.Query(Parse.Installation);
queryAndroid.equalTo('deviceType', 'android');
Parse.Push.send({
where: queryAndroid,
data: {
alert: "Your suitcase has been filled with tiny robots!"
}
});
// Notification for iOS users
var queryIOS = new Parse.Query(Parse.Installation);
queryIOS.equalTo(‘deviceType’, ‘ios');
Parse.Push.send({
where: queryIOS,
data: {
alert: "Your suitcase has been filled with tiny robots!"
}
});
Android端末のみに
通知を送る
iOS端末のみに
通知を送る
144.
// Notification for Windows 8 users
var queryWindows = new Parse.Query(Parse.Installation);
queryWindows.equalTo('deviceType', 'winrt');
Parse.Push.send({
where: queryWindows,
data: {
alert: "Your suitcase has been filled with tiny glass!"
}
});
// Notification for Windows Phone 8 users
var queryWindowsPhone = new Parse.Query(Parse.Installation);
queryWindowsPhone.equalTo('deviceType', 'winphone');
Parse.Push.send({
where: queryWindowsPhone,
data: {
alert: "Your suitcase is very hip; very metro."
}
});
WinRT端末のみに
通知を送る
WindowsPhone端末
のみに通知を送る
145.
ParseQuery query = ParseInstallation.getQuery();
query.whereEqualTo("channels", "suitcaseOwners");
// Notification for Android users
query.whereEqualTo("deviceType", "android");
ParsePush androidPush = new ParsePush();
androidPush.setMessage("Your suitcase has been filled with tiny robots!");
androidPush.setQuery(query);
androidPush.sendPushInBackground();
// Notification for iOS users
query.whereEqualTo("deviceType", "ios");
ParsePush iOSPush = new ParsePush();
iOSPush.setMessage("Your suitcase has been filled with tiny apples!");
iOSPush.setQuery(query);
iOSPush.sendPushInBackground();
Targeting by Platform
146.
// Notification for Windows 8 users
query.whereEqualTo("deviceType", "winrt");
ParsePush winPush = new ParsePush();
winPush.setMessage("Your suitcase has been filled with tiny glass!");
winPush.setQuery(query);
winPush.sendPushInBackground();
// Notification for Windows Phone 8
usersquery.whereEqualTo("deviceType", "winphone");
ParsePush wpPush = new ParsePush();
wpPush.setMessage("Your suitcase is very hip; very metro.");
wpPush.setQuery(query);
wpPush.sendPushInBackground();
147.
// Notification for Android users
var androidPush = new ParsePush();
androidPush.Alert = "Your suitcase has been filled with tiny robots!";
androidPush.Query =
from installation in ParseInstallation.Query
where installation.Channels.Contains("suitcaseOwners")
where installation.DeviceType == "android"
select installation;
await androidPush.SendAsync();
// Notification for iOS usersvar iOSPush = new ParsePush();
iosPush.Alert = "Your suitcase has been filled with tiny apples!";
iosPush.Query =
from installation in ParseInstallation.Query
where installation.Channels.Contains("suitcaseOwners")
where installation.DeviceType == "ios"
select installation;
await iosPush.SendAsync();
// …….
148.
PFQuery *query = [PFInstallation query];
[query whereKey:@"channels" equalTo:@"suitcaseOwners"];
// Notification for Android users
[query whereKey:@"deviceType" equalTo:@"android"];
PFPush *androidPush = [[PFPush alloc] init];
[androidPush setMessage:@"Your suitcase has been filled with tiny
robots!"];
[androidPush setQuery:query];
[androidPush sendPushInBackground];
// Notification for iOS users
[query whereKey:@"deviceType" equalTo:@"ios"];
PFPush *iOSPush = [[PFPush alloc] init];[iOSPush
setMessage:@"Your suitcase has been filled with tiny apples!"];
[iOSPush setChannel:@"suitcaseOwners"];
[iOSPush setQuery:query];
[iOSPush sendPushInBackground];
// ……
149.
Pushのスケジューリング
var query = new Parse.Query(Parse.Installation);
query.equalTo('user_id', 'user_123');
Parse.Push.send({
where: query,
data: {
alert: "You previously created a reminder for the game today"
},
push_time: new Date(2015, 5, 10)
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
2015年5月10日に、
通知を送るように
スケジュールする
154.
Cloud Codeの実行 run
Parse.Cloud.run('hello',
{}, {
success: function(result) {
// result is 'Hello world!'
},
error: function(error) {
}
});
ParseCloud.callFunctionInBackground("hello",
new HashMap<String, Object>(),
new FunctionCallback<String>() {
void done(String result, ParseException e) {
if (e == null) {
// result is "Hello world!”
}
}
});
155.
var result =
await
ParseCloud.CallFunctionAsync<IDictionary<string, object>>(
"hello", new Dictionary<string, object>()
);
// result is "Hello world!"
Cloud Codeの実行
[PFCloud
callFunctionInBackground:@"hello"
withParameters:@{}
block:^(NSString *result, NSError *error) {
if (!error) {
// result is @"Hello world!"
}
}];
156.
Cloud Codeサンプル
Parse.Cloud.define("averageStars",
function( request, response ) {
var query = new Parse.Query("Review");
query.equalTo("movie", request.params.movie);
query.find({
success: function(results) {
var sum = 0;
for (var i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
response.success(sum / results.length);
},
error: function() {
response.error("movie lookup failed");
}
});
});
映画のレビューの
平均値を計算する
157.
sendPushToUser
Parse.Cloud.define(“sendPushToUser”,
function(request, response) {
var senderUser = request.user;
var recipientUserId = request.params.recipientId;
var message = request.params.message;
// メッセージを送っていいのかのチェックをする (友達にのみ送る)
// ユーザーは、友達の情報を持っている
if (senderUser.get("friendIds").indexOf(recipientUserId) === -1) {
response.error(
"The recipient is not the sender's friend, cannot send push.”
);
}
// メッセージが140文字以内かのチェックをする
if (message.length > 140) {
// 長すぎたら切り詰めて後ろに“...”を置く
message = message.substring(0, 137) + "...";
}
友人にショート
メッセージを送る
158.
// 受け取り手の送り先を調べる
var recipientUser = new Parse.User();
recipientUser.id = recipientUserId;
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("user", recipientUser);
// Push通知を行う
Parse.Push.send({
where: pushQuery,
data: {
alert: message
}
}).then(function() {
response.success("Push was sent successfully.")
}, function(error) {
response.error("Push failed to send with error: " + error.message);
});
});
友人にショート
メッセージを送る
159.
ショート・メッセージの送信
[PFCloud callFunctionInBackground:@"sendPushToUser"
withParameters:@{@"recipientId": userObject.id, @"message": message}
block:^(NSString *success, NSError *error) {
if (!error) { // Push sent successfully }
}];
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("recipientId", userObject.getObjectId());
params.put("message", message);
ParseCloud.callFunctionInBackground("sendPushToUser", params,
new FunctionCallback<String>() {
void done(String success, ParseException e) {
if (e == null) {
// Push sent successfully
}
}
});
161.
beforeSave Triggers
Parse.Cloud.beforeSave("Review",
function(request, response) {
if (request.object.get("stars") < 1) {
response.error("you cannot give less than one star");
} else if (request.object.get("stars") > 5) {
response.error("you cannot give more than five stars");
} else {
response.success();
}
});
Parse.Cloud.beforeSave(Parse.User,
function(request, response) {
if (!request.object.get("email")) {
response.error("email is required for signup");
} else {
response.success();
}
});
妥当性チェック:
星の数は、1から5まで
妥当性チェック:サインアップ
には、e-mailが必要
162.
保存時にデータを変更する
Parse.Cloud.beforeSave("Review",
function(request, response) {
var comment = request.object.get("comment");
if (comment.length > 140) {
// Truncate and add a ...
request.object.set("comment", comment.substring(0, 137) + "...");
}
response.success();
});
コメントの長さを、140文字以内
に切り詰めてから保存する
201.
Data mutation
データの変化 mutationはFluxのActionのやり方で
dispatch される。それで、多くの異なるコンポーネント同
士が話しかけるviewを要求することなしに、更新を同期
することが可能になる。標準的なParseのデータの変化は
、全てサポートされている。
// Create a new Comment object with some initial data
ParseReact.Mutation.Create('Comment', {
text: 'Parse <3 React’
}).dispatch();
202.
Mutationを生成するためには、最初に適当なコンストラクタを
呼ばなければならない。これらのメソッドは、Mutations API
で見ることができる。いったん Mutation が生成されれば、それ
は、dispatch()を呼ぶことで実行される。
// Create a new Pizza object
var creator = ParseReact.Mutation.Create('Pizza’, {
toppings: [ 'sausage', 'peppers' ],
crust: 'deep dish'
});
// ...and execute it
creator. dispatch();
Be the first to comment