diff --git a/WidgetDestroyer.xcodeproj/project.pbxproj b/WidgetDestroyer.xcodeproj/project.pbxproj index 309d498..ab7e77f 100644 --- a/WidgetDestroyer.xcodeproj/project.pbxproj +++ b/WidgetDestroyer.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + C112618B2B4989530038C671 /* WidgetDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11261842B492F890038C671 /* WidgetDetector.swift */; }; + C112618D2B498B770038C671 /* UserDefaults+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C112618C2B498B770038C671 /* UserDefaults+Extension.swift */; }; C11CF6282B3345BE0040B273 /* WidgetDestroyerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11CF6272B3345BE0040B273 /* WidgetDestroyerApp.swift */; }; C11CF62A2B3345BE0040B273 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11CF6292B3345BE0040B273 /* ContentView.swift */; }; C11CF62C2B3345BF0040B273 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C11CF62B2B3345BF0040B273 /* Assets.xcassets */; }; @@ -44,6 +46,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + C11261842B492F890038C671 /* WidgetDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDetector.swift; sourceTree = ""; }; + C112618C2B498B770038C671 /* UserDefaults+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extension.swift"; sourceTree = ""; }; C11CF6242B3345BE0040B273 /* WidgetDestroyer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WidgetDestroyer.app; sourceTree = BUILT_PRODUCTS_DIR; }; C11CF6272B3345BE0040B273 /* WidgetDestroyerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDestroyerApp.swift; sourceTree = ""; }; C11CF6292B3345BE0040B273 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -102,6 +106,8 @@ children = ( C11CF6272B3345BE0040B273 /* WidgetDestroyerApp.swift */, C11CF6292B3345BE0040B273 /* ContentView.swift */, + C11261842B492F890038C671 /* WidgetDetector.swift */, + C112618C2B498B770038C671 /* UserDefaults+Extension.swift */, C11CF62B2B3345BF0040B273 /* Assets.xcassets */, ); path = WidgetDestroyer; @@ -228,7 +234,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C112618B2B4989530038C671 /* WidgetDetector.swift in Sources */, C11CF62A2B3345BE0040B273 /* ContentView.swift in Sources */, + C112618D2B498B770038C671 /* UserDefaults+Extension.swift in Sources */, C11CF6282B3345BE0040B273 /* WidgetDestroyerApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/WidgetDestroyer/ContentView.swift b/WidgetDestroyer/ContentView.swift index d6038e7..0a609bc 100644 --- a/WidgetDestroyer/ContentView.swift +++ b/WidgetDestroyer/ContentView.swift @@ -10,10 +10,20 @@ import WidgetKit struct ContentView: View { + @State private var widgetsInUse: [WidgetFamily] = [] + @Environment(\.scenePhase) private var scenePhase var body: some View { VStack { + GroupBox("Widgets in use") { + ForEach(widgetsInUse, id: \.self) { widget in + Text(widget.description) + .font(.body) + } + } + .padding() + Image(systemName: "arrow.clockwise") .imageScale(.large) @@ -21,9 +31,15 @@ struct ContentView: View { } .font(.largeTitle) .onChange(of: scenePhase) { _, newPhase in - if newPhase == .background { + if newPhase == .active { + WidgetDetector.shared.detect() // ✅ + // just to display the current widgets + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + widgetsInUse = Array(UserDefaults.standard.get(\.widgetsInUse)) + .sorted(by: { $0.description.count < $1.description.count }) + } + } else if newPhase == .background { WidgetCenter.shared.reloadAllTimelines() - print("♻️ Widget reload") } } } diff --git a/WidgetDestroyer/UserDefaults+Extension.swift b/WidgetDestroyer/UserDefaults+Extension.swift new file mode 100644 index 0000000..3d00648 --- /dev/null +++ b/WidgetDestroyer/UserDefaults+Extension.swift @@ -0,0 +1,34 @@ +// +// UserDefaults+Extension.swift +// WidgetDestroyer +// +// Created by 유재호 on 1/6/24. +// + +import WidgetKit + +extension WidgetFamily: Codable { } + +extension UserDefaults { + + struct MyData: Codable { + var widgetsInUse = Set() + } + + func set(_ keyPath: WritableKeyPath, _ value: T) { + var myData = MyData() + myData[keyPath: keyPath] = value + let asData = try? JSONEncoder().encode(myData) + set(asData, forKey: "MyData") + } + + func get(_ keyPath: KeyPath) -> T { + guard + let asData = data(forKey: "MyData"), + let myData = try? JSONDecoder().decode(MyData.self, from: asData) + else { + return MyData()[keyPath: keyPath] // returns default value + } + return myData[keyPath: keyPath] + } +} diff --git a/WidgetDestroyer/WidgetDetector.swift b/WidgetDestroyer/WidgetDetector.swift new file mode 100644 index 0000000..3acccd2 --- /dev/null +++ b/WidgetDestroyer/WidgetDetector.swift @@ -0,0 +1,54 @@ +// +// WidgetDetector.swift +// NumberWidgetExtension +// +// Created by 유재호 on 1/6/24. +// + +import WidgetKit + +final class WidgetDetector { + + /// Singleton instance of WidgetDetector + static let shared = WidgetDetector() + + private init() { } + + func detect() { + WidgetCenter.shared.getCurrentConfigurations { result in + if case .success(let widgetInfo) = result { + self.diff(widgetInfo) + } + } + } + + private func diff(_ newValue: [WidgetInfo]) { + let cachedWidgetsInUse = UserDefaults.standard.get(\.widgetsInUse) + let newWidgetsInUse = Set(newValue.map(\.family)) + + guard newWidgetsInUse != cachedWidgetsInUse else { + return // ignores if the same + } + + let addedWidgets = newWidgetsInUse.subtracting(cachedWidgetsInUse) + let removedWidgets = cachedWidgetsInUse.subtracting(newWidgetsInUse) + + for added in addedWidgets { + didAddWidget(family: added) + } + + for removed in removedWidgets { + didRemoveWidget(family: removed) + } + + UserDefaults.standard.set(\.widgetsInUse, newWidgetsInUse) + } + + private func didAddWidget(family: WidgetFamily) { + print("🟢\(family) widget added!") + } + + private func didRemoveWidget(family: WidgetFamily) { + print("🔴\(family) widget removed!") + } +}