Add logging, clean up
This commit is contained in:
parent
c1fd129ccc
commit
aa4e047752
6 changed files with 110 additions and 72 deletions
|
|
@ -32,6 +32,8 @@
|
|||
9474F2132834755A00CDE4DD /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474F20B283321C300CDE4DD /* Notification.swift */; };
|
||||
9474F2142834755E00CDE4DD /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474F1FE28316ACE00CDE4DD /* Subscription.swift */; };
|
||||
9474F2152834758700CDE4DD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474F211283327C200CDE4DD /* Helpers.swift */; };
|
||||
9474F217283531A300CDE4DD /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474F216283531A200CDE4DD /* Log.swift */; };
|
||||
94E9196C28353E0100F30170 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474F216283531A200CDE4DD /* Log.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -83,6 +85,7 @@
|
|||
9474F20B283321C300CDE4DD /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
|
||||
9474F20E283326C500CDE4DD /* ApiService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiService.swift; sourceTree = "<group>"; };
|
||||
9474F211283327C200CDE4DD /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||
9474F216283531A200CDE4DD /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -202,6 +205,7 @@
|
|||
children = (
|
||||
9474F20E283326C500CDE4DD /* ApiService.swift */,
|
||||
9474F211283327C200CDE4DD /* Helpers.swift */,
|
||||
9474F216283531A200CDE4DD /* Log.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -315,6 +319,7 @@
|
|||
files = (
|
||||
9474F1F92830835400CDE4DD /* Store.swift in Sources */,
|
||||
9474F212283327C200CDE4DD /* Helpers.swift in Sources */,
|
||||
9474F217283531A300CDE4DD /* Log.swift in Sources */,
|
||||
9474F20928331F3A00CDE4DD /* NotificationListView.swift in Sources */,
|
||||
9474F20A28331F3A00CDE4DD /* NotificationRowView.swift in Sources */,
|
||||
9474F1D2282F2D2C00CDE4DD /* AppDelegate.swift in Sources */,
|
||||
|
|
@ -335,6 +340,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9474F2132834755A00CDE4DD /* Notification.swift in Sources */,
|
||||
94E9196C28353E0100F30170 /* Log.swift in Sources */,
|
||||
9474F2152834758700CDE4DD /* Helpers.swift in Sources */,
|
||||
9474F1E7282F3FFD00CDE4DD /* NotificationService.swift in Sources */,
|
||||
9474F2052831D51500CDE4DD /* Store.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import CoreData
|
|||
// https://stackoverflow.com/questions/47374903/viewing-core-data-data-from-your-app-on-a-device
|
||||
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
let tag = "AppDelegate"
|
||||
let store = Store.shared
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
print("ApplicationDelegate didFinishLaunchingWithOptions.")
|
||||
Log.d(tag, "ApplicationDelegate didFinishLaunchingWithOptions.")
|
||||
// FirebaseApp.configure() DOES NOT WORK
|
||||
FirebaseConfiguration.shared.setLoggerLevel(.max)
|
||||
Messaging.messaging().delegate = self
|
||||
|
|
@ -24,23 +24,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
registerForPushNotifications()
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
|
||||
|
||||
|
||||
print("Documents Directory: ", FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last ?? "Not Found!")
|
||||
|
||||
// Check if launched from notification
|
||||
let notificationOption = launchOptions?[.remoteNotification]
|
||||
|
||||
// 1
|
||||
if
|
||||
let notification = notificationOption as? [String: AnyObject],
|
||||
let aps = notification["aps"] as? [String: AnyObject] {
|
||||
print("there is a new item")
|
||||
// 2
|
||||
|
||||
// 3
|
||||
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -108,33 +91,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy var persistentContainer: NSPersistentContainer = {
|
||||
let container = NSPersistentContainer(name: "Model")
|
||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||
if let error = error as NSError? {
|
||||
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
})
|
||||
return container
|
||||
}()
|
||||
|
||||
// MARK: - Core Data Saving support
|
||||
|
||||
func saveContext () {
|
||||
let context = persistentContainer.viewContext
|
||||
if context.hasChanges {
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
let nserror = error as NSError
|
||||
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||
|
|
@ -144,8 +100,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
|||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
||||
) {
|
||||
let userInfo = notification.request.content.userInfo
|
||||
print("willPresent", userInfo)
|
||||
|
||||
Log.d(tag, "Notification received via userNotificationCenter(willPresent)", userInfo)
|
||||
store.saveNotification(fromUserInfo: userInfo)
|
||||
completionHandler([[.alert, .sound]])
|
||||
}
|
||||
|
|
@ -155,18 +110,21 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
|
|||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
print("didReceive")
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
|
||||
Log.d(tag, "Notification received via userNotificationCenter(didReceive)", userInfo)
|
||||
store.saveNotification(fromUserInfo: userInfo)
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
||||
// [END ios_10_message_handling]
|
||||
extension AppDelegate: MessagingDelegate {
|
||||
// [START refresh_token]
|
||||
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
|
||||
print("Firebase registration token: \(String(describing: fcmToken))")
|
||||
func messaging(
|
||||
_ messaging: Messaging,
|
||||
didReceiveRegistrationToken fcmToken: String?
|
||||
) {
|
||||
Log.d(tag, "Firebase token received: \(String(describing: fcmToken))")
|
||||
|
||||
// FIXME: Is this necessary?
|
||||
|
||||
let dataDict: [String: String] = ["token": fcmToken ?? ""]
|
||||
NotificationCenter.default.post(
|
||||
|
|
@ -174,8 +132,6 @@ extension AppDelegate: MessagingDelegate {
|
|||
object: nil,
|
||||
userInfo: dataDict
|
||||
)
|
||||
// TODO: If necessary send token to application server.
|
||||
// Note: This callback is fired at each app startup and whenever a new token is generated.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,17 @@ import CoreData
|
|||
class Store: ObservableObject {
|
||||
static let shared = Store()
|
||||
|
||||
let tag = "Store"
|
||||
let container: NSPersistentContainer
|
||||
var context: NSManagedObjectContext
|
||||
|
||||
init() {
|
||||
let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.io.heckel.ntfy")!
|
||||
let storeUrl = directory.appendingPathComponent("ntfy.sqlite")
|
||||
let description = NSPersistentStoreDescription(url: storeUrl)
|
||||
|
||||
container = NSPersistentContainer(name: "Model")
|
||||
container.persistentStoreDescriptions = [description]
|
||||
container.loadPersistentStores { description, error in
|
||||
if let error = error {
|
||||
print("Core Data failed to load: \(error.localizedDescription)")
|
||||
|
|
@ -31,7 +37,6 @@ class Store: ObservableObject {
|
|||
try? context.save()
|
||||
}
|
||||
|
||||
|
||||
func getSubscription(baseUrl: String, topic: String) -> Subscription? {
|
||||
let fetchRequest = Subscription.fetchRequest()
|
||||
let baseUrlPredicate = NSPredicate(format: "baseUrl = %@", baseUrl)
|
||||
|
|
@ -44,7 +49,7 @@ class Store: ObservableObject {
|
|||
|
||||
func saveNotification(fromUserInfo userInfo: [AnyHashable: Any]) {
|
||||
guard let id = userInfo["id"] as? String,
|
||||
let topic = userInfo["topic"] as? String,
|
||||
let topic = userInfo["topic"] as? String, // FIXME: Notification should also contain baseUrl
|
||||
let time = userInfo["time"] as? String,
|
||||
let timeInt = Int64(time),
|
||||
let message = userInfo["message"] as? String else {
|
||||
|
|
@ -65,7 +70,7 @@ class Store: ObservableObject {
|
|||
subscription.addToNotifications(notification)
|
||||
try context.save()
|
||||
} catch let error {
|
||||
print(error)
|
||||
Log.w(tag, "Cannot store notification", error)
|
||||
context.rollback()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,32 @@
|
|||
//
|
||||
// ApiService.swift
|
||||
// ntfy.sh
|
||||
//
|
||||
// Created by Andrew Cope on 2/16/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ApiService: NSObject {
|
||||
static let shared = ApiService()
|
||||
|
||||
let tag = "ApiService"
|
||||
|
||||
func poll(subscription: Subscription, completionHandler: @escaping ([Message]?, Error?) -> Void) {
|
||||
let lastNotificationTime = 0 //subscription.lastNotification()?.timestamp ?? 0 // FIXME
|
||||
guard let url = URL(string: subscription.urlString()) else { return }
|
||||
let lastNotificationTime = subscription.lastNotification()?.time ?? 0
|
||||
let sinceString = lastNotificationTime > 0 ? String(lastNotificationTime) : "all";
|
||||
let urlString = "\(subscription.urlString())/json?poll=1&since=\(sinceString)"
|
||||
let urlString = "\(url)/json?poll=1&since=\(sinceString)"
|
||||
|
||||
Log.d(tag, "Polling from \(urlString)")
|
||||
fetchJsonData(urlString: urlString, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
func publish(subscription: Subscription, message: String, title: String, priority: Int = 3, tags: [String] = [], completionHandler: @escaping (Notification?, Error?) -> Void) {
|
||||
func publish(
|
||||
subscription: Subscription,
|
||||
message: String,
|
||||
title: String,
|
||||
priority: Int = 3,
|
||||
tags: [String] = [],
|
||||
completionHandler: @escaping (Notification?, Error?) -> Void
|
||||
) {
|
||||
guard let url = URL(string: subscription.urlString()) else { return }
|
||||
var request = URLRequest(url: url)
|
||||
|
||||
Log.d(tag, "Publishing to \(url)")
|
||||
|
||||
request.httpMethod = "POST"
|
||||
request.setValue(title, forHTTPHeaderField: "Title")
|
||||
request.setValue(String(priority), forHTTPHeaderField: "Priority")
|
||||
|
|
@ -35,7 +41,7 @@ class ApiService: NSObject {
|
|||
|
||||
private func fetchJsonData<T: Decodable>(urlString: String, completionHandler: @escaping ([T]?, Error?) -> ()) {
|
||||
guard let url = URL(string: urlString) else { return }
|
||||
var request = URLRequest(url: url)
|
||||
let request = URLRequest(url: url)
|
||||
|
||||
URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
if let error = error {
|
||||
|
|
|
|||
62
ntfy/Utils/Log.swift
Normal file
62
ntfy/Utils/Log.swift
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// Log.swift
|
||||
// ntfy
|
||||
//
|
||||
// Created by Philipp Heckel on 5/18/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Log {
|
||||
static var dateFormat = "yyyy-MM-dd hh:mm:ss.SSSSSSZ"
|
||||
static var dateFormatter: DateFormatter {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = dateFormat
|
||||
formatter.locale = Locale.current
|
||||
formatter.timeZone = TimeZone.current
|
||||
return formatter
|
||||
}
|
||||
|
||||
static func d(_ tag: String, _ message: String, _ other: Any...) {
|
||||
log(.debug, tag, message, other)
|
||||
}
|
||||
|
||||
static func i(_ tag: String, _ message: String, _ other: Any...) {
|
||||
log(.info, tag, message, other)
|
||||
}
|
||||
|
||||
static func w(_ tag: String, _ message: String, _ other: Any...) {
|
||||
log(.warning, tag, message, other)
|
||||
}
|
||||
|
||||
static func e(_ tag: String, _ message: String, _ other: Any...) {
|
||||
log(.error, tag, message, other)
|
||||
}
|
||||
|
||||
static func log(_ level: LogLevel, _ tag: String, _ message: String, _ other: Any...) {
|
||||
print("\(dateStr()) ntfyApp [\(levelStr(level))] \(tag): \(message)")
|
||||
if !other.isEmpty {
|
||||
print(other)
|
||||
}
|
||||
}
|
||||
|
||||
static func dateStr() -> String {
|
||||
return dateFormatter.string(from: Date())
|
||||
}
|
||||
|
||||
static func levelStr(_ level: LogLevel) -> String {
|
||||
switch level {
|
||||
case .debug: return "DEBUG"
|
||||
case .info: return "INFO"
|
||||
case .warning: return "WARNING ⚠️"
|
||||
case .error: return "ERROR ‼️"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LogLevel {
|
||||
case debug
|
||||
case info
|
||||
case warning
|
||||
case error
|
||||
}
|
||||
|
|
@ -14,7 +14,10 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
override func didReceive(
|
||||
_ request: UNNotificationRequest,
|
||||
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
||||
) {
|
||||
self.contentHandler = contentHandler
|
||||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue