푸시 알림 앱 아이콘 이미지 변경

apwierk·7일 전
0

TIL

목록 보기
34/34

swift notification appicon image

위 사진과 같이 채팅 앱의 경우 기존에는 앱 아이콘이 있어야되는 위치에 상대방의 프로필 사진이 올라오고,
상대방이 올린 이미지가 우측에 나오는 경우가 있다.

이 경우를 구현하기로했다.

레퍼런스가 없어서 찾는데 오래걸렸다.

여러 테스트 결과

NotificationServiceExtension을 이용하여 구현 가능하다.

  • File - New - target... - Notification Service Extension 을 클릭한다.

  • 이름을 NotificationServiceExtension로 만든다.

  • 파일 내부에 NotificationService.swift 파일이 생성되는데 해당 내부 오버라이드된 함수 didReceive() 내부에 재정의하면 이미지를 설정할 수 있다.

  • didReceive()함수는 푸시를 받았을 때 발동 된다.

  • 파라미터 중 escaping closer인 contentHandler클로저를 실행할 경우 푸시 알림이 동작된다.

  • 파라미터 중 request는 payload내부에 포함된 정보를 가져올 수 있다.

{
  "message": {
    "token": "fLDngNy_fE2HvmHJ79Xm03:APA9...(푸시토큰)",
    "notification": {
      "title": "테스트!",
      "body": "이미지가 갔니??"
    },
    "data": {
        "image": "https://static01.nyt.com/athletic/uploads/wp/2024/10/01072609/diogo-jota-scaled-e1727782003173-1024x683.jpg",
        "iconUrl": "https://imgnews.pstatic.net/image/311/2025/01/25/0001820500_001_20250125092306669.jpg"
    },
    "apns": {
      "payload": {
        "aps": {
          "mutable-content": 1
        }
      }
    }
  }
}

위 payload 처럼 data 내부에 첨부할 이미지(image), 아이콘 이미지(iconUrl)을 같이 보낸다.

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

	// 푸시가 오면 발동!
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
       self.contentHandler = contentHandler // contentHandler 실행 시 푸시 알림 발동!
        
// iconUrl key가 있다면 앱 아이콘 변경 실행
       if let attachmentURLAsString = request.content.userInfo["iconUrl"] as? String { 
           changeAppIconToImage(request: request, iconUrl: attachmentURLAsString, contentHandler: contentHandler)
       }
    }
    
    private func changeAppIconToImage(request: UNNotificationRequest, iconUrl: String, contentHandler: @escaping (UNNotificationContent) -> Void) {
        
        fetchImageData(from: iconUrl) { data in
            let avatar = INImage(imageData: data!) // 이미지(데이터) 설정
            
            let senderPerson = INPerson(
                personHandle: INPersonHandle(value: "unique-sender-id-2", type: .unknown),
                nameComponents: nil,
                displayName: request.content.title, // 타이틀을 이름으로 설정
                image: avatar, // 이미지 아바타 설정
                contactIdentifier: nil,
                customIdentifier: nil,
                isMe: false,
                suggestionType: .none
            )
            
            let mePerson = INPerson(
                personHandle: INPersonHandle(value: "unique-me-id-2", type: .unknown),
                nameComponents: nil,
                displayName: nil,
                image: avatar,
                contactIdentifier: nil,
                customIdentifier: nil,
                isMe: true,
                suggestionType: .none
            )
            
            
            let intent = INSendMessageIntent(recipients: [mePerson],
                                             outgoingMessageType: .outgoingMessageText,
                                             content: "Message content",
                                             speakableGroupName: nil,
                                             conversationIdentifier: "unique-conversation-id-1",
                                             serviceName: nil,
                                             sender: senderPerson,
                                             attachments: nil)
            
            
            intent.setImage(avatar, forParameterNamed: \.sender)
            
            
            let interaction = INInteraction(intent: intent, response: nil)
            interaction.direction = .incoming
            
            
            interaction.donate { error in
            if let error = error {
                print(error)
                return
            }
            
            do {
                // 이전 notification에 intent를 더해주고, 노티 띄우기
                let updatedContent = try request.content.updating(from: intent)
                contentHandler(updatedContent)
            } catch {
                print(error)
            }
        }
    }
    
    func fetchImageData(from urlString: String, completion: @escaping (Data?) -> Void) {
        guard let url = URL(string: urlString) else {
            completion(nil)
            return
        }

        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data, error == nil {
                completion(data)
            } else {
                completion(nil)
            }
        }
        task.resume()
    }
}
  1. INImage(imageData: ) 이용하여 이미지 설정

  2. INPerson(...) 타입으로 sender, me 설정 (sender-displayname에 title 설정)

  3. INSendMessageIntent에 내용 입력

  4. intent와 interaction 인스턴스 생성


여기까지 하고 푸시를 보내면 아이콘이 바뀌기 시작한다.

아이콘만변경

여기서 문제 발생..

첨부 이미지와 아이콘 변경 이미지 두 개가 모두 첨부되지 않는 문제 발생

contentHandler를 한 번만 실행해야된다.


interaction.donate { error in
	if let error = error {
		print(error)
		return
	}
            
	if let attachmentURLAsString = request.content.userInfo["image"] as? String,
		let attachmentURL = URL(string: attachmentURLAsString) {
			self.downloadImageFrom(url: attachmentURL) { (attachment) in
			if let attachment = attachment {
                            
				// updatedContent에 intent 적용된 내용이 있으므로 bestAttemptContent로 반영
				if let updated = try? request.content.updating(from: intent),
				let updatedMutable = updated.mutableCopy() as? UNMutableNotificationContent {
                                
					updatedMutable.attachments = [attachment]
                                
                    // 업데이트된 내용을 bestAttemptContent로 교체
					self.bestAttemptContent = updatedMutable
                                
                    FIRMessagingExtensionHelper().populateNotificationContent(updatedMutable, withContentHandler: contentHandler)
				}
			}
		}
	}
}

위 코드는 updatedMutable에 변경된 아이콘 intent를 업데이트하고,
업데이트된 updatedMutable을 FIRMessagingExtensionHelper을 이용하여 contentHandler를 실행시키는 코드이다. 이렇게 하면 이미지 두 개 모두 첨부 가능하다.

아이콘만변경
profile
iOS 꿈나무 개발자

0개의 댓글