summaryrefslogtreecommitdiff
path: root/www/external/NotificationService.swift
blob: 1fa815581fc1effc46cd22343812373f8aa99c1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Credit: https://github.com/phonegap/phonegap-plugin-push/issues/1347#issuecomment-450206184
import UserNotifications

class NotificationService: UNNotificationServiceExtension {
    
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?
    
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        guard let content = bestAttemptContent else {
            #if DEBUG
            exitGracefully("bestAttemptContent not a UNMutableNotificationContent")
            #endif
            return
        }
        
        DispatchQueue.main.async { [weak self] in
            let userInfo: [AnyHashable: Any] = request.content.userInfo
            content.attachments = self?.attachmentsFor(userInfo) ?? []
            guard let copy = self?.bestAttemptContent else {
                #if DEBUG
                self?.exitGracefully("bestAttemptContent is nil")
                #endif
                return
            }
            contentHandler(copy)
        }
    }
    
    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler, let content = self.bestAttemptContent {
            contentHandler(content)
        }
    }
    
    /// Helper function to extract Attachments from an userInfo object
    ///
    /// - Attribute userInfo: The user info extracted from the notification,
    ///                       which should contain one of the available keys to create
    ///                       a rich push notification.
    private func attachmentsFor(_ userInfo: [AnyHashable: Any]) -> [UNNotificationAttachment] {
        if let attachmentURLString = userInfo["image_url_png"] as? String {
            guard let attachmentURL = URL(string: attachmentURLString),
                let imageData = try? Data(contentsOf: attachmentURL),
                let attachment = self.save("image.png", data: imageData, options: nil) else {
                    #if DEBUG
                    self.exitGracefully("PNG was not saved properly")
                    #endif
                    return []
            }
            return [attachment]
        } else if let attachmentURLString = userInfo["image_url_jpg"] as? String {
            guard let attachmentURL = URL(string: attachmentURLString),
                let imageData = try? Data(contentsOf: attachmentURL),
                let attachment = self.save("image.jpg", data: imageData, options: nil) else {
                    #if DEBUG
                    self.exitGracefully("JPG was not saved properly")
                    #endif
                    return []
            }
            return [attachment]
        } else if let attachmentURLString = userInfo["image_url_gif"] as? String {
            guard let attachmentURL = URL(string: attachmentURLString),
                let imageData = try? Data(contentsOf: attachmentURL),
                let attachment = self.save("image.gif", data: imageData, options: nil) else {
                    #if DEBUG
                    self.exitGracefully("GIF was not saved properly")
                    #endif
                    return []
            }
            return [attachment]
        }
        
        return []
    }
    
    /// Save data object onto disk and return an optional attachment linked to this path.
    ///
    /// - Attributes:
    ///   - identifier: The unique identifier of the attachment.
    ///                 Use this string to identify the attachment later. If you specify an
    ///                 empty string, this method creates a unique identifier string for you.
    ///   - data: The data stored onto disk.
    ///   - options: A dictionary of options related to the attached file.
    ///              Use the options to specify meta information about the attachment,
    ///              such as the clipping rectangle to use for the resulting thumbnail.
    private func save(_ identifier: String, data: Data, options: [AnyHashable: Any]?) -> UNNotificationAttachment? {
        // Create paths
        let directory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString, isDirectory: true)
        let fileURL = directory.appendingPathComponent(identifier)
        // Write data on disk
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
        try? data.write(to: fileURL, options: [])
        // Create Notification attachment
        return try? UNNotificationAttachment(identifier: identifier, url: fileURL, options: options)
    }
    
    /// Something went wrong, so maybe we want to clean up and present a fallback to the user.
    ///
    /// - Attribute reason: The reason why something went wrong.
    private func exitGracefully(_ reason: String = "") {
        guard let copy = bestAttemptContent?.mutableCopy() as? UNMutableNotificationContent else { return }
        copy.title = reason
        contentHandler?(copy)
    }
    
}