Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ struct DatabaseExtensionsTests {
// Then
#expect(customization != nil, "Should return a customization object")
#expect(database.fetch(CustomizationDB.self).count == 1, "Should delete duplicates, leaving only one")
#expect(customization?.tabs == [.settings], "Should return the first customization")
}

// MARK: - Update isPatrao Date Calculation Tests
Expand Down
223 changes: 206 additions & 17 deletions MacMagazine/MacMagazine.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
wasCreatedForAppExtension = "YES"
version = "2.0">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
Expand All @@ -16,9 +15,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9BDD424C2EE9E82F00A9BD85"
BuildableName = "WidgetExtension.appex"
BlueprintName = "WidgetExtension"
BlueprintIdentifier = "9BA8288B2EEC7AAA00F26FCB"
BuildableName = "WatchApp.app"
BlueprintName = "WatchApp"
ReferencedContainer = "container:MacMagazine.xcodeproj">
</BuildableReference>
</BuildActionEntry>
Expand Down Expand Up @@ -47,59 +46,38 @@
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9B9E9DC32E986F8400255820"
BuildableName = "MacMagazine.app"
BlueprintName = "MacMagazine"
BlueprintIdentifier = "9BA8288B2EEC7AAA00F26FCB"
BuildableName = "WatchApp.app"
BlueprintName = "WatchApp"
ReferencedContainer = "container:MacMagazine.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "systemMedium"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9B9E9DC32E986F8400255820"
BuildableName = "MacMagazine.app"
BlueprintName = "MacMagazine"
BlueprintIdentifier = "9BA8288B2EEC7AAA00F26FCB"
BuildableName = "WatchApp.app"
BlueprintName = "WatchApp"
ReferencedContainer = "container:MacMagazine.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>WatchApp.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
<key>WatchkApp Watch App (Notification).xcscheme_^#shared#^_</key>
<dict>
Expand All @@ -34,14 +34,14 @@
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>Widget.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>88C8B2E52EF476E100044DE9</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>9B9E9DC32E986F8400255820</key>
<dict>
<key>primary</key>
Expand All @@ -52,6 +52,11 @@
<key>primary</key>
<true/>
</dict>
<key>9BDD424C2EE9E82F00A9BD85</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
22 changes: 22 additions & 0 deletions MacMagazine/WatchApp/MainApp/WatchApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import FeedLibrary
import StorageLibrary
import SwiftData
import SwiftUI
import UserNotifications
import WatchKit

@main
struct WatchApp: App {

private let database = Database(models: [FeedDB.self], inMemory: false)

@WKApplicationDelegateAdaptor(WatchNotificationsDelegate.self)
private var notifDelegate

var body: some Scene {
WindowGroup {
FeedMainView(
Expand All @@ -16,6 +21,23 @@ struct WatchApp: App {
)
)
.modelContainer(database.sharedModelContainer)
.onOpenURL { url in
handleDeepLink(url)
}
}
}

@MainActor
private func handleDeepLink(_ url: URL) {
guard url.scheme == "macmagazine" else { return }

let host = url.host ?? ""
let pathComponents = url.pathComponents.filter { $0 != "/" }

guard host == "news" else { return }

if pathComponents.count >= 2, pathComponents[0] == "post" {
_ = pathComponents[1]
}
}
}
55 changes: 55 additions & 0 deletions MacMagazine/WatchApp/MainApp/WatchNotificationsDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Foundation
import UserNotifications
import WatchKit
import WidgetKit

final class WatchNotificationsDelegate: NSObject,
WKApplicationDelegate,
UNUserNotificationCenterDelegate {

func applicationDidFinishLaunching() {
UNUserNotificationCenter.current().delegate = self
}

func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse
) async {
handlePush(userInfo: response.notification.request.content.userInfo)
}

// MARK: - Push handling

private func handlePush(userInfo: [AnyHashable: Any]) {
guard
let postId = userInfo["postId"] as? String,
let title = userInfo["title"] as? String,
!postId.isEmpty,
!title.isEmpty
else {
return
}

let date: Date = {
if let time = userInfo["date"] as? TimeInterval {
return Date(timeIntervalSince1970: time)
}
if let iso = userInfo["date"] as? String,
let date = ISO8601DateFormatter().date(from: iso) {
return date
}
return Date()
}()

MacMagazineWidgetSharedStore.write(
post: .init(
id: postId,
title: title,
date: date
)
)

// Atualiza as complicações
WidgetCenter.shared.reloadTimelines(ofKind: "WatchWidget")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
41 changes: 35 additions & 6 deletions MacMagazine/WatchApp/ViewModel/FeedMainViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
import StorageLibrary
import SwiftData
import WatchKit
import WidgetKit

@MainActor
@Observable
Expand All @@ -12,7 +13,6 @@ final class FeedMainViewModel {

private(set) var status: FeedViewModel.Status = .loading
var selectedIndex: Int = 0
var showActions: Bool = false
var showContextMenu: Bool = false
var selectedPostForDetail: FeedDB?
private(set) var isRefreshing: Bool = false
Expand All @@ -22,6 +22,7 @@ final class FeedMainViewModel {
private let feedViewModel: FeedViewModel

// MARK: - Init

init(feedViewModel: FeedViewModel) {
self.feedViewModel = feedViewModel
status = feedViewModel.status
Expand All @@ -32,19 +33,38 @@ final class FeedMainViewModel {
func refresh(modelContext: ModelContext) async {
guard !isRefreshing else { return }
isRefreshing = true

defer {
isRefreshing = false
}
defer { isRefreshing = false }

_ = try? await feedViewModel.getWatchFeed()
status = feedViewModel.status

persistLatestPostSnapshot(from: modelContext)

WidgetCenter.shared.reloadTimelines(ofKind: "WatchWidget")
}

func toggleFavorite(post: FeedDB, modelContext: ModelContext) {
post.favorite.toggle()
try? modelContext.save()
showActions = true
}

// MARK: - Snapshot para Widget

private func persistLatestPostSnapshot(from modelContext: ModelContext) {
var descriptor = FetchDescriptor<FeedDB>(
sortBy: [SortDescriptor(\.pubDate, order: .reverse)]
)
descriptor.fetchLimit = 1

guard let last = try? modelContext.fetch(descriptor).first else { return }

MacMagazineWidgetSharedStore.write(
post: .init(
id: last.postId,
title: last.title,
date: last.pubDate
)
)
}

// MARK: - Index / Helpers
Expand Down Expand Up @@ -74,6 +94,15 @@ final class FeedMainViewModel {
guard quantity > 0 else { return 0 }
return min(max(index, 0), quantity - 1)
}

func openPost(withId postId: String, modelContext: ModelContext) {
let predicate = #Predicate<FeedDB> { $0.postId == postId }
let descriptor = FetchDescriptor<FeedDB>(predicate: predicate)

if let post = try? modelContext.fetch(descriptor).first {
selectedPostForDetail = post
}
}
}

#if DEBUG
Expand Down
14 changes: 12 additions & 2 deletions MacMagazine/WatchApp/Views/FeedMainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,17 @@ struct FeedMainView: View {
var body: some View {
NavigationStack {
rootContent
.navigationDestination(item: $viewModel.selectedPostForDetail) { payload in
FeedDetailView(viewModel: viewModel, post: payload)
.navigationDestination(item: $viewModel.selectedPostForDetail) { post in
FeedDetailView(viewModel: viewModel, post: post)
}
.onOpenURL { url in
guard url.scheme == "macmagazine", url.host == "news" else { return }

let parts = url.pathComponents.filter { $0 != "/" }
if parts.count >= 2, parts[0] == "post" {
let postId = parts[1]
viewModel.openPost(withId: postId, modelContext: modelContext)
}
}
}
.task {
Expand Down Expand Up @@ -193,6 +202,7 @@ struct FeedMainView: View {
)
.frame(maxHeight: .infinity, alignment: .trailing)
.opacity(viewModel.isRefreshing ? 0 : 1)
.padding(.trailing, 4)
}
.overlay {
if viewModel.isRefreshing {
Expand Down
2 changes: 1 addition & 1 deletion MacMagazine/WatchApp/Views/Row/FeedRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct FeedRowView: View {
.foregroundStyle(.primary)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(3)
.padding(.trailing, 12)
.padding(.trailing, 22)

Text(post.dateText)
.font(.caption2)
Expand Down
4 changes: 4 additions & 0 deletions MacMagazine/WatchApp/WatchApp.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@
<array>
<string>CloudKit</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.brit.beta.macmagazine</string>
</array>
</dict>
</plist>
Loading