Push notifications - kuimoani/defold GitHub Wiki

Push notifications

이 λ¬Έμ„œλŠ” λ‹Ήμ‹ μ˜ κ²Œμž„ λ˜λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 둜컬 λ˜λŠ” 원격 푸쉬 μ•Œλ¦Όμ„ κ΅¬ν˜„ν•˜κ³  μ„€μ •ν•˜λŠ” 방법에 λŒ€ν•΄ μ„€λͺ…ν•©λ‹ˆλ‹€.

푸쉬 μ•Œλ¦Όμ€ iOS와 Android μž₯μΉ˜μ—μ„œ μ‚¬μš© κ°€λŠ₯ν•˜λ©° μ—…λ°μ΄νŠΈλ‚˜ 변경사항을 ν”Œλ ˆμ΄μ–΄μ—κ²Œ μ•Œλ¦¬λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€. 핡심 κΈ°λŠ₯은 iOS와 Androidκ°€ λΉ„μŠ·ν•œ κΈ°λŠ₯을 κ°€μ§€κ³  μžˆμ§€λ§Œ ν”Œλž«νΌκ°„ κ³ λ €ν•΄μ•Όν•  λͺ‡λͺ‡ 차이점이 μžˆμŠ΅λ‹ˆλ‹€.

푸쉬 μ•Œλ¦Όμ„ μ„œλ²„μ—μ„œ λŒ€μƒ μž₯치둜 보내렀면, 앱에 νŠΉμ • μ •λ³΄μ˜ λΉ„νŠΈ(bits)κ°€ ν•„μš”ν•©λ‹ˆλ‹€. κ°€μž₯ λ³΅μž‘ν•œ 뢀뢄은 λ³΄μ•ˆ μ •λ³΄λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμœΌλ―€λ‘œ μ•Œλ¦Ό μ‹œμŠ€ν…œμ€ μ•Œλ¦Όμ„ μˆ˜μ‹ ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μ •λ‹Ήν•œμ§€(legitimity)λ₯Ό 인증할 수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μ•Œλ¦Ό μ„œλ²„μ—μ„œ λ³΄μ•ˆ 정보가 ν•„μš”ν•  수 도 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ Apple λ˜λŠ” Google μ„œλ²„κ°€ λ‹Ήμ‹ μ˜ μ„œλ²„κ°€ μ •λ‹Ήν•œ μ•Œλ¦Ό μ†‘μ‹ μž(notification sender)인지λ₯Ό 인증할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ, μ•Œλ¦Όμ„ 보낼 경우, νŠΉμ • μ‚¬μš©μžμ˜ μž₯치둜 μ•Œλ¦Όμ„ μ§μ ‘μ μœΌλ‘œ κ³ μœ ν•˜κ²Œ 보낼 수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. 이λ₯Ό μœ„ν•΄ νŠΉμ • λ””λ°”μ΄μŠ€(μ‚¬μš©μž)μ—κ²Œ κ³ μœ ν•œ 토큰을 μ‚¬μš©ν•˜κ±°λ‚˜ 탐색해야 ν•©λ‹ˆλ‹€.

iOS setup

Apple 푸쉬 μ•Œλ¦Ό μ„œλΉ„μŠ€(Push Notification Service)에 λŒ€ν•΄ μ•Œκ³  μ‹Άλ‹€λ©΄, Apple 의 λ¬Έμ„œλ₯Ό 읽고 μ„œλΉ„μŠ€κ°€ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ λ¨Όμ € μ•Œμ•„λ³΄λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html λ₯Ό μ°Έκ³  λ°”λžλ‹ˆλ‹€.

iOSμ—μ„œλŠ” μ•Œλ¦Όμ„ 보내기 μœ„ν•΄ λ‹€μŒ 정보가 ν•„μš”ν•©λ‹ˆλ‹€.

  • App ID μ—μ„œ 푸쉬 μ•Œλ¦Όμ„ ν™œμ„±ν™” ν•΄μ•Ό 함
  • μœ νš¨ν•œ App IDλ₯Ό ν¬ν•¨ν•œ ν”„λ‘œλΉ„μ Έλ‹ ν”„λ‘œνŒŒμΌλ„ ν•„μš”ν•¨
  • Apple 푸쉬 μ•Œλ¦Ό μ„œλΉ„μŠ€μ˜ SSL μΈμ¦μ„œ(Certificate)κ°€ μžˆμ–΄μ•Ό Apple μ„œλ²„μ— μ•Œλ¦Ό 데이터λ₯Ό 보낼 수 있음

이 λͺ¨λ“  것을 μœ„ν•΄μ„œλŠ” Apple Developer Member center 둜 μ΄λ™ν•΄μ„œ 푸쉬 μ•Œλ¦Όμ„ ν™œμ„±ν™” ν•˜κΈ° μœ„ν•œ AppIλ₯Ό μˆ˜μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€.

AppID push notifications

λ˜ν•œ Apple 푸쉬 μ•Œλ¦Ό μ„œλΉ„μŠ€μ˜ SSL μΈμ¦μ„œ(Certificate)λ₯Ό 생성해야 ν•©λ‹ˆλ‹€.

APN SSL certificate

푸쉬 μ•Œλ¦Όμ„ 보낼 μ„œλ²„μ—λŠ” μΈμ¦μ„œκ°€ ν•„μš”ν•©λ‹ˆλ‹€. 개발 μ€‘μ—λŠ” μΈμ¦μ„œλ₯Ό λ‹€μš΄λ‘œλ“œ, μ„€μΉ˜ν•˜κ³  APNS-Pusher λ˜λŠ” NWPusher κ³Ό 같은 푸쉬 ν…ŒμŠ€νŠΈ 앱을 μ‹€ν–‰ν•΄ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

AppIDμ—μ„œ μƒˆ ν”„λ‘œλΉ„μ Έλ‹ ν”„λ‘œνŒŒμΌμ„ μƒμ„±ν•˜κ³  λ‹Ήμ‹ μ˜ μž₯μΉ˜μ— λ„£λŠ” μž‘μ—…μ„ ν•΄μ•Ό ν•©λ‹ˆλ‹€. Xcodeλ₯Ό ν†΅ν•˜κ±°λ‚˜ "Member Center" νŽ˜μ΄μ§€μ—μ„œ 직접 μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Provisioning profile

Apple의 μƒŒλ“œλ°•μŠ€ μ„œλ²„κ°€ μ—…λ°μ΄νŠΈ λ˜λŠ” λ™μ•ˆ μ‹œκ°„μ΄ κ±Έλ¦¬λ―€λ‘œ 푸쉬 μž‘μ—…μ΄ μ¦‰μ‹œ λ™μž‘ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

이제 λͺ‡ κ°€μ§€ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ‹€ν–‰ν•΄ λ΄…μ‹œλ‹€.

local function push_listener(self, payload, origin)
	-- νŽ˜μ΄λ‘œλ“œ(payload)κ°€ 도착함
	pprint(payload)
end

function init(self)
	local sysinfo = sys.get_sys_info()
	if sysinfo.system_name == "Android" then
		msg.post("#", "push_android")
	elseif sysinfo.system_name == "iPhone OS" then
		msg.post("#", "push_ios")
	end
end

function on_message(self, message_id, message)
	if message_id == hash("push_ios") then
		local alerts = {push.NOTIFICATION_BADGE, push.NOTIFICATION_SOUND, push.NOTIFICATION_ALERT}
		push.register(alerts, function (self, token, error)
			if token then
				local t = ""
				for i = 1,#token do
					t = t .. string.format("%02x", string.byte(token, i))
				end
				-- device token 좜λ ₯
				print(t)
			else
				-- Error
				print(error.error)
			end
		end)
		push.set_listener(push_listener)
	elseif message_id == hash("push_android") then
		push.register(nil, function (self, token, error)
			if token then
				-- device token 좜λ ₯
				print(token)
			else
				-- Error
				print(error.error)
			end
		end)
		push.set_listener(push_listener)
	end
end

λͺ¨λ‘ 잘 λ™μž‘ν•˜λ©΄ μ•Œλ¦Ό λ¦¬μŠ€λ„ˆ(notification listener)κ°€ λ“±λ‘λ˜κ³  μ‚¬μš©ν•  수 μžˆλŠ” 토큰(token)을 μ–»κ²Œ λ©λ‹ˆλ‹€.

DEBUG:SCRIPT: 1f8ba7869b84b10df69a07aa623cd7f55f62bca22cef61b51fedac643ec61ad8

푸쉬 ν…ŒμŠ€νŠΈ 앱을 싀행쀑인 경우, device tokenκ³Ό APN service SSL certificate을 μ‚¬μš©ν•˜μ—¬ λ‹Ήμ‹ μ˜ μž₯μΉ˜μ— μ•Œλ¦Όμ„ 보내 보도둝 ν•©λ‹ˆλ‹€.

Pusher test

μ•Œλ¦Όμ€ ν…ŒμŠ€νŠΈ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄μ—μ„œ push_listener() ν•¨μˆ˜μ— λ„λ‹¬ν•œ ν›„ μ¦‰μ‹œ ν΄λΌμ΄μ–ΈνŠΈμ— 도착해야 ν•©λ‹ˆλ‹€.

DEBUG:SCRIPT:
{
  aps = {
    badge = 42,
    alert = Testing.. (1),
    sound = default,
  }
}

그러면 iOS ν™ˆ ν™”λ©΄μ—μ„œ μ•„λž˜μ™€ 같이 λ‚˜νƒ€λ‚©λ‹ˆλ‹€.

iOS notification

μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄μ—μ„œ 뱃지 개수(badge count)λ₯Ό μ—…λ°μ΄νŠΈ ν•˜λ €λ©΄, push.set_badge_count() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

Android setup

Google 은 Google Cloud Messaging 에 λŒ€ν•œ 폭넓은 λ¬Έμ„œλ₯Ό κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€. https://developers.google.com/cloud-messaging/gcm λΆ€ν„° 읽어 λ³΄μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

Androidμ—μ„œλŠ” μ•Œλ¦Όμ„ 보내렀면 μ•„λž˜μ™€ 같은 정보가 ν•„μš”ν•©λ‹ˆλ‹€.

  • μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ— λ‚΄μž₯λ˜λŠ” GCM Sender ID
  • Google μ„œλ²„λ₯Ό 톡해 μ•Œλ¦Ό 전솑을 ν™œμ„±ν™” ν•˜κΈ° μœ„ν•œ Server API Key

이 섀정은 맀우 κ°„λ‹¨ν•˜λ©° http://developers.google.com μ—μ„œ "Android"λ₯Ό 클릭 ν›„ "Google Cloud Messaging" ν•­λͺ©μ„ μ°Έκ³ ν•˜μ—¬ μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Android getting started

νŽ˜μ΄μ§€ ν•˜λ‹¨μ—λŠ” "Get a configuration file" μ΄λΌλŠ” λ²„νŠΌμ΄ μžˆμŠ΅λ‹ˆλ‹€.

Android configuration file

이 λ²„νŠΌμ„ ν΄λ¦­ν•˜κ³  λ‹€μŒ μˆœμ„œλ₯Ό λ”°λΌν•˜λ©΄ λ§ˆμ§€λ§‰μ— Server API Key 와 Sender ID λ₯Ό νšλ“ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Android cloud services info

Sender ID λ₯Ό λ³΅μ‚¬ν•΄μ„œ Defold ν”„λ‘œμ νŠΈ μ…‹νŒ…μ˜ gcm_sender_id ν•„λ“œμ— λΆ™μ—¬λ„£κΈ° ν•©λ‹ˆλ‹€.

Google Cloud Messaging sender ID

이제 ν΄λΌμ΄μ–ΈνŠΈμ˜ λͺ¨λ“  μ€€λΉ„κ°€ λλ‚¬μŠ΅λ‹ˆλ‹€. μœ„μ˜ μ½”λ“œ μ˜ˆμ œλŠ” Androidμ—μ„œλ„ 잘 λ™μž‘ν•˜λ―€λ‘œ 이λ₯Ό μ‹€ν–‰ν•˜κ³  device token idλ₯Ό 볡사해 λ‘‘λ‹ˆλ‹€.

DEBUG:SCRIPT: APA91bHkcKm0QHAMUCEQ_Dlpq2gzset6vh0cz46kDDV6230C5rFivyWZMCxGXcjxRDKg1PK4z1kWg3xnUVqSDiO_4_RiG8b8HeYJfaoW1ho4ukWYXjq5RE0Sy-JTyrhqRusUP_BxRTcE

이제 μš°λ¦¬κ°€ ν•„μš”ν•œ μ •λ³΄λŠ” λͺ¨λ‘ μ–»μ—ˆμŠ΅λ‹ˆλ‹€. Google의 μ•Œλ¦Όμ€ Web API λ₯Ό 톡해 μ „μ†‘λ˜λ―€λ‘œ curl 을 μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈ λ©”μ„Έμ§€λ₯Ό 전솑할 수 μžˆμŠ΅λ‹ˆλ‹€.

$ curl  -X POST  -H "Content-type: application/json"  -H 'Authorization: key=SERVER_KEY' -d '{"registration_ids" : ["TOKEN_ID"], "data": {"alert": "Hello"}}' https://android.googleapis.com/gcm/send

SERVER_KEY κ³Ό TOKEN_ID λ₯Ό 당신이 받은 ν‚€λ‘œ λ°”κΏ”μ„œ μ‹€ν–‰ν•˜μ„Έμš”.

Local push notifications

원격 μ•Œλ¦Ό λΏλ§Œμ•„λ‹ˆλΌ 둜컬 푸쉬 μ•Œλ¦Όλ„ μ§€μ›λ©λ‹ˆλ‹€. μ•½κ°„μ˜ μ„€μ •μœΌλ‘œ 둜컬 μ•Œλ¦Όμ„ μŠ€μΌ€μ₯΄λ§ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

-- 3초 λ‚΄λ‘œ 둜컬 푸쉬 μŠ€μΌ€μ€„
local payload = '{"data" : {"field" : "Some value", "field2" : "Other value"}}'
id, err = push.schedule(3, "A notification!", "Hello there", payload, { action = "get going" })

이 idλŠ” μ˜ˆμ•½λœ μ•Œλ¦Όμ„ κ³ μœ ν•˜κ²Œ μ‹λ³„ν•˜κ³  λ‚˜μ€‘μ— μ €μž₯될 수 μžˆμŠ΅λ‹ˆλ‹€. push.schedule() 의 λ§ˆμ§€λ§‰ νŒŒλΌλ©”ν„°λŠ” ν”Œλž«νΌλ³„ 섀정을 ν¬ν•¨ν•˜κ³  μžˆλŠ” ν…Œμ΄λΈ”μž…λ‹ˆλ‹€.

action

(iOS μ „μš©) "slide to unlock(λ°€μ–΄μ„œ μž κΈˆν•΄μ œ)" ν…μŠ€νŠΈμ˜ "unlock(μž κΈˆν•΄μ œ)"λ₯Ό κ΅μ²΄ν•˜λŠ” 언락 μŠ¬λΌμ΄λ”(unlock slider)의 κ°’ λ˜λŠ” μ–ΌλŸΏ(alert)의 였λ₯Έμͺ½ λ²„νŠΌμ˜ νƒ€μ΄ν‹€λ‘œ μ‚¬μš©λ˜λŠ” μ–ΌλŸΏ μ•‘μ…˜ λ¬Έμžμ—΄(alert action string)

badge_count

(iOS μ „μš©) 뱃지 μ•„μ΄μ½˜(icon badge)의 숫자 κ°’. 뱃지λ₯Ό μ‚­μ œν•˜λ €λ©΄ 0으둜 μ„€μ •

priority

(Android μ „μš©) μš°μ„ μˆœμœ„(priority)λŠ” μ•Œλ¦Όμ„ ν‘œμ‹œν•˜λŠ” 방법에 λŒ€ν•΄ μž₯치 UI에 μ•Œλ €μ£ΌλŠ” νžŒνŠΈμž…λ‹ˆλ‹€. μ—¬κΈ°μ—” -2 ~ 2 μ‚¬μ΄μ˜ κ°’μœΌλ‘œ 5λ‹¨κ³„μ˜ μš°μ„ μˆœμœ„ 레벨이 μžˆμŠ΅λ‹ˆλ‹€. νŠΉλ³„νžˆ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ 2λ₯Ό κΈ°λ³Έ μš°μ„ μˆœμœ„ λ‹¨κ³„λ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€.

Inspecting scheduled notifications

API λŠ” ν˜„μž¬ μŠ€μΌ€μ₯΄λœ μ•Œλ¦Όμ„ κ²€μ‚¬ν•˜κΈ° μœ„ν•œ 두 κ°€μ§€ ν•¨μˆ˜λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

n = push.get_scheduled(id)
pprint(n)

id둜 μŠ€μΌ€μ₯΄λœ μ•Œλ¦Όμ˜ 상세 정보λ₯Ό ν¬ν•¨ν•˜λŠ” ν…Œμ΄λΈ”μ„ λ¦¬ν„΄ν•©λ‹ˆλ‹€.

DEBUG:SCRIPT:
{
  payload = {"data":{"field":"Some value","field2":"Other value"}},
  title = A notification!,
  priority = 2,
  seconds = 19.991938,
  message = Hello there,
}

seconds λŠ” μ•Œλ¦Όμ΄ μ‹œμž‘λ˜λŠ”λ° 남은 μ‹œκ°„(초)λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€. λ˜ν•œ 전체 μ•Œλ¦Ό μŠ€μΌ€μ₯΄μ„ ν¬ν•¨ν•œ ν…Œμ΄λΈ”μ„ νƒμƒ‰ν•˜λŠ” 것도 κ°€λŠ₯ν•©λ‹ˆλ‹€.

all_n = push.get_all_scheduled()
pprint(all_n)

μ•Œλ¦Ό id둜 λ‚˜μ—΄λœ 데이터λ₯Ό ν¬ν•¨ν•˜λŠ” ν…Œμ΄λΈ”μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.

DEBUG:SCRIPT:
{
  0 = {
    payload = {"data":{"field":"Some value","field2":"Other value"}},
    title = A notification!,
    priority = 2,
    seconds = 6.009774,
    message = Hey hey,
  }
  1 = {
    payload = {"data":{"field":"Some value","field2":"Other value"}},
    title = Another notification!,
    priority = 2,
    seconds = 12.652521,
    message = Hello there,
  }
  2 = {
    payload = {"data":{"field":"Some value","field2":"Other value"}},
    title = Hey, much notification!,
    priority = 2,
    seconds = 15.553719,
    message = Please answer!,
  }
}