завел уведы вроде как ура
This commit is contained in:
parent
d053ffccc6
commit
e78c66b0c8
@ -550,9 +550,31 @@ class APIClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let threads = try JSONDecoder().decode([Thread].self, from: data)
|
let currentThreads = try JSONDecoder().decode([Thread].self, from: data)
|
||||||
let newThreads = threads.filter { $0.id > lastKnownThreadId }
|
|
||||||
completion(.success(newThreads))
|
let savedThreadsKey = "savedThreads_\(boardCode)"
|
||||||
|
let savedThreadsData = UserDefaults.standard.data(forKey: savedThreadsKey)
|
||||||
|
|
||||||
|
if let savedThreadsData = savedThreadsData,
|
||||||
|
let savedThreads = try? JSONDecoder().decode([Thread].self, from: savedThreadsData) {
|
||||||
|
|
||||||
|
let savedThreadIds = Set(savedThreads.map { $0.id })
|
||||||
|
let newThreads = currentThreads.filter { !savedThreadIds.contains($0.id) }
|
||||||
|
|
||||||
|
if !newThreads.isEmpty {
|
||||||
|
print("Найдено \(newThreads.count) новых тредов в /\(boardCode)/")
|
||||||
|
}
|
||||||
|
|
||||||
|
completion(.success(newThreads))
|
||||||
|
} else {
|
||||||
|
print("Первая синхронизация для /\(boardCode)/ - сохраняем \(currentThreads.count) тредов")
|
||||||
|
completion(.success([]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let encodedData = try? JSONEncoder().encode(currentThreads) {
|
||||||
|
UserDefaults.standard.set(encodedData, forKey: savedThreadsKey)
|
||||||
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
completion(.failure(error))
|
completion(.failure(error))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,11 +24,12 @@ class BackgroundTaskManager {
|
|||||||
func scheduleBackgroundTask() {
|
func scheduleBackgroundTask() {
|
||||||
let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
|
let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
|
||||||
let settings = Settings()
|
let settings = Settings()
|
||||||
let interval = TimeInterval(settings.notificationInterval)
|
let interval = TimeInterval(settings.notificationInterval * 60)
|
||||||
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
|
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try BGTaskScheduler.shared.submit(request)
|
try BGTaskScheduler.shared.submit(request)
|
||||||
|
print("Фоновая задача запланирована на \(interval) секунд")
|
||||||
} catch {
|
} catch {
|
||||||
print("Не удалось запланировать фоновую задачу: \(error)")
|
print("Не удалось запланировать фоновую задачу: \(error)")
|
||||||
}
|
}
|
||||||
@ -51,20 +52,18 @@ class BackgroundTaskManager {
|
|||||||
for boardCode in notificationManager.subscribedBoards {
|
for boardCode in notificationManager.subscribedBoards {
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
let lastKnownId = UserDefaults.standard.integer(forKey: "lastThreadId_\(boardCode)")
|
APIClient().checkNewThreads(forBoard: boardCode, lastKnownThreadId: 0) { result in
|
||||||
|
|
||||||
APIClient().checkNewThreads(forBoard: boardCode, lastKnownThreadId: lastKnownId) { result in
|
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let newThreads):
|
case .success(let newThreads):
|
||||||
if !newThreads.isEmpty {
|
if !newThreads.isEmpty {
|
||||||
hasNewThreads = true
|
hasNewThreads = true
|
||||||
|
print("Найдено \(newThreads.count) новых тредов в /\(boardCode)/")
|
||||||
for thread in newThreads {
|
for thread in newThreads {
|
||||||
self.notificationManager.scheduleNotification(for: thread, boardCode: boardCode)
|
self.notificationManager.scheduleNotification(for: thread, boardCode: boardCode)
|
||||||
}
|
}
|
||||||
UserDefaults.standard.set(newThreads.first?.id ?? lastKnownId, forKey: "lastThreadId_\(boardCode)")
|
|
||||||
}
|
}
|
||||||
case .failure:
|
case .failure(let error):
|
||||||
break
|
print("Ошибка проверки новых тредов для /\(boardCode)/: \(error)")
|
||||||
}
|
}
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import UserNotifications
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct MobileMkchApp: App {
|
struct MobileMkchApp: App {
|
||||||
@ -22,6 +23,25 @@ struct MobileMkchApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setupNotifications() {
|
||||||
|
UNUserNotificationCenter.current().delegate = NotificationDelegate.shared
|
||||||
|
notificationManager.requestPermission { granted in
|
||||||
|
if granted {
|
||||||
|
print("Разрешения на уведомления получены")
|
||||||
|
} else {
|
||||||
|
print("Разрешения на уведомления отклонены")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleNotificationLaunch() {
|
||||||
|
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||||
|
let userInfo = scene.session.userInfo {
|
||||||
|
print("Приложение запущено из уведомления")
|
||||||
|
}
|
||||||
|
notificationManager.clearBadge()
|
||||||
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
Group {
|
Group {
|
||||||
@ -38,6 +58,8 @@ struct MobileMkchApp: App {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
BackgroundTaskManager.shared.registerBackgroundTasks()
|
BackgroundTaskManager.shared.registerBackgroundTasks()
|
||||||
setupBackgroundTasks()
|
setupBackgroundTasks()
|
||||||
|
setupNotifications()
|
||||||
|
handleNotificationLaunch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,18 @@ import Foundation
|
|||||||
import UserNotifications
|
import UserNotifications
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
|
||||||
|
static let shared = NotificationDelegate()
|
||||||
|
|
||||||
|
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||||
|
completionHandler([.banner, .sound, .badge])
|
||||||
|
}
|
||||||
|
|
||||||
|
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||||
|
completionHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NotificationManager: ObservableObject {
|
class NotificationManager: ObservableObject {
|
||||||
static let shared = NotificationManager()
|
static let shared = NotificationManager()
|
||||||
|
|
||||||
@ -17,11 +29,20 @@ class NotificationManager: ObservableObject {
|
|||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.isNotificationsEnabled = granted
|
self.isNotificationsEnabled = granted
|
||||||
|
if granted {
|
||||||
|
self.registerForRemoteNotifications()
|
||||||
|
}
|
||||||
completion(granted)
|
completion(granted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func registerForRemoteNotifications() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIApplication.shared.registerForRemoteNotifications()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func checkNotificationStatus() {
|
func checkNotificationStatus() {
|
||||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@ -33,36 +54,90 @@ class NotificationManager: ObservableObject {
|
|||||||
func subscribeToBoard(_ boardCode: String) {
|
func subscribeToBoard(_ boardCode: String) {
|
||||||
subscribedBoards.insert(boardCode)
|
subscribedBoards.insert(boardCode)
|
||||||
saveSubscribedBoards()
|
saveSubscribedBoards()
|
||||||
|
|
||||||
BackgroundTaskManager.shared.scheduleBackgroundTask()
|
BackgroundTaskManager.shared.scheduleBackgroundTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func syncThreadsForBoard(_ boardCode: String) {
|
||||||
|
let url = URL(string: "https://mkch.pooziqo.xyz/api/board/\(boardCode)")!
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.setValue("MobileMkch/2.0.0-ios-alpha", forHTTPHeaderField: "User-Agent")
|
||||||
|
|
||||||
|
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||||
|
if let data = data,
|
||||||
|
let threads = try? JSONDecoder().decode([Thread].self, from: data) {
|
||||||
|
let savedThreadsKey = "savedThreads_\(boardCode)"
|
||||||
|
if let encodedData = try? JSONEncoder().encode(threads) {
|
||||||
|
UserDefaults.standard.set(encodedData, forKey: savedThreadsKey)
|
||||||
|
print("Синхронизировано \(threads.count) тредов для /\(boardCode)/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.resume()
|
||||||
|
}
|
||||||
|
|
||||||
func unsubscribeFromBoard(_ boardCode: String) {
|
func unsubscribeFromBoard(_ boardCode: String) {
|
||||||
subscribedBoards.remove(boardCode)
|
subscribedBoards.remove(boardCode)
|
||||||
saveSubscribedBoards()
|
saveSubscribedBoards()
|
||||||
|
|
||||||
|
let savedThreadsKey = "savedThreads_\(boardCode)"
|
||||||
|
UserDefaults.standard.removeObject(forKey: savedThreadsKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func scheduleNotification(for thread: Thread, boardCode: String) {
|
func scheduleNotification(for thread: Thread, boardCode: String) {
|
||||||
|
guard isNotificationsEnabled else { return }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = "Новый тред"
|
content.title = "Новый тред"
|
||||||
content.body = "\(thread.title) в /\(boardCode)/"
|
content.body = "\(thread.title) в /\(boardCode)/"
|
||||||
content.sound = .default
|
content.sound = .default
|
||||||
|
content.badge = 1
|
||||||
|
|
||||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||||
let request = UNNotificationRequest(identifier: "thread_\(thread.id)", content: content, trigger: trigger)
|
let request = UNNotificationRequest(identifier: "thread_\(thread.id)_\(boardCode)", content: content, trigger: trigger)
|
||||||
|
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request) { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Ошибка планирования уведомления: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func scheduleTestNotification() {
|
func scheduleTestNotification() {
|
||||||
|
guard isNotificationsEnabled else { return }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = "Тестовое уведомление"
|
content.title = "Тестовое уведомление"
|
||||||
content.body = "Новый тред: Тестовый тред в /test/"
|
content.body = "Новый тред: Тестовый тред в /test/"
|
||||||
content.sound = .default
|
content.sound = .default
|
||||||
|
content.badge = 1
|
||||||
|
|
||||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
|
||||||
let request = UNNotificationRequest(identifier: "test_notification", content: content, trigger: trigger)
|
let request = UNNotificationRequest(identifier: "test_notification", content: content, trigger: trigger)
|
||||||
|
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request) { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Ошибка планирования тестового уведомления: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearAllNotifications() {
|
||||||
|
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
|
||||||
|
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearBadge() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearAllSavedThreads() {
|
||||||
|
for boardCode in subscribedBoards {
|
||||||
|
let savedThreadsKey = "savedThreads_\(boardCode)"
|
||||||
|
UserDefaults.standard.removeObject(forKey: savedThreadsKey)
|
||||||
|
}
|
||||||
|
print("Очищены все сохраненные треды")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadSubscribedBoards() {
|
private func loadSubscribedBoards() {
|
||||||
|
|||||||
@ -7,6 +7,8 @@ struct NotificationSettingsView: View {
|
|||||||
@State private var showingPermissionAlert = false
|
@State private var showingPermissionAlert = false
|
||||||
@State private var boards: [Board] = []
|
@State private var boards: [Board] = []
|
||||||
@State private var isLoadingBoards = false
|
@State private var isLoadingBoards = false
|
||||||
|
@State private var isCheckingThreads = false
|
||||||
|
@State private var showingTestNotification = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
@ -40,7 +42,7 @@ struct NotificationSettingsView: View {
|
|||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
Text("Уведомления находятся в бета-версии и могут работать нестабильно. Функция может работать нестабильно или не работать ВОВСЕ.")
|
Text("Уведомления находятся в бета-версии и могут работать нестабильно.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@ -71,12 +73,59 @@ struct NotificationSettingsView: View {
|
|||||||
}
|
}
|
||||||
.onChange(of: settings.notificationInterval) { _ in
|
.onChange(of: settings.notificationInterval) { _ in
|
||||||
settings.saveSettings()
|
settings.saveSettings()
|
||||||
|
BackgroundTaskManager.shared.scheduleBackgroundTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button("Проверить новые треды сейчас") {
|
Button(action: {
|
||||||
checkNewThreadsNow()
|
showingTestNotification = true
|
||||||
|
notificationManager.scheduleTestNotification()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "bell.badge")
|
||||||
|
Text("Отправить тестовое уведомление")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
|
.disabled(!notificationManager.isNotificationsEnabled)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
isCheckingThreads = true
|
||||||
|
checkNewThreadsNow()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
if isCheckingThreads {
|
||||||
|
ProgressView()
|
||||||
|
.scaleEffect(0.8)
|
||||||
|
} else {
|
||||||
|
Image(systemName: "arrow.clockwise")
|
||||||
|
}
|
||||||
|
Text("Проверить новые треды сейчас")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.disabled(isCheckingThreads || notificationManager.subscribedBoards.isEmpty)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
syncAllBoards()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
|
Text("Синхронизировать все доски")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(.orange)
|
||||||
|
.disabled(notificationManager.subscribedBoards.isEmpty)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
notificationManager.clearAllSavedThreads()
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "trash")
|
||||||
|
Text("Очистить все сохраненные треды")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(.red)
|
||||||
|
.disabled(notificationManager.subscribedBoards.isEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +138,9 @@ struct NotificationSettingsView: View {
|
|||||||
Text("Загрузка досок...")
|
Text("Загрузка досок...")
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
} else if boards.isEmpty {
|
||||||
|
Text("Не удалось загрузить доски")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
} else {
|
} else {
|
||||||
ForEach(boards) { board in
|
ForEach(boards) { board in
|
||||||
HStack {
|
HStack {
|
||||||
@ -117,6 +169,22 @@ struct NotificationSettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: Text("Статус")) {
|
||||||
|
HStack {
|
||||||
|
Text("Разрешения")
|
||||||
|
Spacer()
|
||||||
|
Text(notificationManager.isNotificationsEnabled ? "Включены" : "Отключены")
|
||||||
|
.foregroundColor(notificationManager.isNotificationsEnabled ? .green : .red)
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("Подписки")
|
||||||
|
Spacer()
|
||||||
|
Text("\(notificationManager.subscribedBoards.count) досок")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,6 +207,11 @@ struct NotificationSettingsView: View {
|
|||||||
} message: {
|
} message: {
|
||||||
Text("Для получения уведомлений о новых тредах необходимо разрешить уведомления в настройках")
|
Text("Для получения уведомлений о новых тредах необходимо разрешить уведомления в настройках")
|
||||||
}
|
}
|
||||||
|
.alert("Тестовое уведомление", isPresented: $showingTestNotification) {
|
||||||
|
Button("OK") { }
|
||||||
|
} message: {
|
||||||
|
Text("Тестовое уведомление отправлено. Проверьте, получили ли вы его.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,25 +227,57 @@ extension NotificationSettingsView {
|
|||||||
private func checkNewThreadsNow() {
|
private func checkNewThreadsNow() {
|
||||||
guard !notificationManager.subscribedBoards.isEmpty else { return }
|
guard !notificationManager.subscribedBoards.isEmpty else { return }
|
||||||
|
|
||||||
|
let group = DispatchGroup()
|
||||||
|
var foundNewThreads = false
|
||||||
|
|
||||||
for boardCode in notificationManager.subscribedBoards {
|
for boardCode in notificationManager.subscribedBoards {
|
||||||
let lastKnownId = UserDefaults.standard.integer(forKey: "lastThreadId_\(boardCode)")
|
group.enter()
|
||||||
|
|
||||||
apiClient.checkNewThreads(forBoard: boardCode, lastKnownThreadId: lastKnownId) { result in
|
apiClient.checkNewThreads(forBoard: boardCode, lastKnownThreadId: 0) { result in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let newThreads):
|
case .success(let newThreads):
|
||||||
if !newThreads.isEmpty {
|
if !newThreads.isEmpty {
|
||||||
|
foundNewThreads = true
|
||||||
for thread in newThreads {
|
for thread in newThreads {
|
||||||
notificationManager.scheduleNotification(for: thread, boardCode: boardCode)
|
notificationManager.scheduleNotification(for: thread, boardCode: boardCode)
|
||||||
}
|
}
|
||||||
UserDefaults.standard.set(newThreads.first?.id ?? lastKnownId, forKey: "lastThreadId_\(boardCode)")
|
|
||||||
}
|
}
|
||||||
case .failure:
|
case .failure(let error):
|
||||||
break
|
print("Ошибка проверки тредов для /\(boardCode)/: \(error)")
|
||||||
}
|
}
|
||||||
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group.notify(queue: .main) {
|
||||||
|
isCheckingThreads = false
|
||||||
|
if !foundNewThreads {
|
||||||
|
print("Новых тредов не найдено")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func syncAllBoards() {
|
||||||
|
for boardCode in notificationManager.subscribedBoards {
|
||||||
|
let savedThreadsKey = "savedThreads_\(boardCode)"
|
||||||
|
UserDefaults.standard.removeObject(forKey: savedThreadsKey)
|
||||||
|
|
||||||
|
let url = URL(string: "https://mkch.pooziqo.xyz/api/board/\(boardCode)")!
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.setValue("MobileMkch/2.0.0-ios-alpha", forHTTPHeaderField: "User-Agent")
|
||||||
|
|
||||||
|
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||||
|
if let data = data,
|
||||||
|
let threads = try? JSONDecoder().decode([Thread].self, from: data) {
|
||||||
|
if let encodedData = try? JSONEncoder().encode(threads) {
|
||||||
|
UserDefaults.standard.set(encodedData, forKey: savedThreadsKey)
|
||||||
|
print("Синхронизировано \(threads.count) тредов для /\(boardCode)/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.resume()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadBoards() {
|
private func loadBoards() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user