Add logging, clean up

This commit is contained in:
Philipp Heckel 2022-05-18 10:51:43 -04:00
parent c1fd129ccc
commit aa4e047752
6 changed files with 110 additions and 72 deletions

View file

@ -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 */,

View file

@ -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.
}
}

View file

@ -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()
}
}

View file

@ -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
View 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
}

View file

@ -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)