ویجت ها و اکستنشن ها (Widgets & Extensions)
ویجت (Widget) کارت کوچک اطلاعات است. سریع دیده می شود. اکستنشن (Extension) بخش اضافه برنامه است. کار خاصی انجام می دهد. با گروه های برنامه داده کوچک را مشترک کن. سپس با WidgetKit نمایش بده. مثل تابلو اعلانات مدرسه که خلاصه ها را نشان می دهد.
Widget timeline
یک تایم لاین بساز. سپس در زمان های مختلف مقدار نشان بده. از TimelineProvider استفاده کن.
import WidgetKit
import SwiftUI
struct TimeEntry: TimelineEntry {
let date: Date
}
struct TimeProvider: TimelineProvider {
func placeholder(in context: Context) -> TimeEntry {
return TimeEntry(date: Date())
}
func getSnapshot(in context: Context, completion: @escaping (TimeEntry) -> Void) {
completion(TimeEntry(date: Date()))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<TimeEntry>) -> Void) {
let entries: [TimeEntry] = stride(from: 0, through: 60 * 30, by: 60).map { offset in
let d: Date = Date().addingTimeInterval(Double(offset))
return TimeEntry(date: d)
}
completion(Timeline(entries: entries, policy: .atEnd))
}
}
struct TimeWidgetView: View {
var entry: TimeEntry
var body: some View {
Text(entry.date, style: .time)
.font(.headline)
}
}
@main
struct TimeWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "TimeWidget", provider: TimeProvider()) { e in
TimeWidgetView(entry: e)
}
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Widget timeline demo")
}
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Share extension skeleton
اسکلت یک Share Extension را بساز. سپس متن ورودی را مدیریت کن. در پایان پست کن.
import SwiftUI
struct ShareExtensionSkeleton: View {
@State private var sharedText: String = ""
var body: some View {
VStack(spacing: 12) {
Text("Incoming content")
TextEditor(text: $sharedText)
.frame(minHeight: 120)
.border(.secondary)
HStack {
Spacer()
Button("Post") {
// handle share
}
}
}
.padding()
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
ShareExtensionSkeleton()
}
}
import SwiftUI
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
App Groups (اشتراک داده با ویجت)
قابلیت App Groups را برای هر دو تارگت فعال کن. سپس با UserDefaults مقدار کوچک را رد و بدل کن.
import Foundation
func updateSharedNotesCount(_ count: Int) {
let defaults: UserDefaults? = UserDefaults(suiteName: "group.com.example.notes")
defaults?.set(count, forKey: "notesCount")
}
import WidgetKit
import SwiftUI
struct CountEntry: TimelineEntry {
let date: Date
let count: Int
}
struct NotesProvider: TimelineProvider {
func placeholder(in context: Context) -> CountEntry {
return CountEntry(date: Date(), count: 0)
}
func getSnapshot(in context: Context, completion: @escaping (CountEntry) -> Void) {
let defaults: UserDefaults? = UserDefaults(suiteName: "group.com.example.notes")
let count: Int = defaults?.integer(forKey: "notesCount") ?? 0
completion(CountEntry(date: Date(), count: count))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<CountEntry>) -> Void) {
let defaults: UserDefaults? = UserDefaults(suiteName: "group.com.example.notes")
let count: Int = defaults?.integer(forKey: "notesCount") ?? 0
let entries: [CountEntry] = [CountEntry(date: Date(), count: count)]
let timeline: Timeline<CountEntry> = Timeline(entries: entries, policy: .after(Date().addingTimeInterval(1800)))
completion(timeline)
}
}
Widgets
تارگت ویجت بساز. سپس Provider و View را پیاده سازی کن. پیکربندی استاتیک برگردان.
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
return SimpleEntry(date: Date())
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
completion(SimpleEntry(date: Date()))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
let timeline: Timeline<SimpleEntry> = Timeline(entries: [SimpleEntry(date: Date())], policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
}
struct MyWidgetEntryView: View {
var entry: SimpleEntry
var body: some View {
Text(entry.date, style: .time)
}
}
@main
struct MyWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "MyWidget", provider: Provider()) { entry in
MyWidgetEntryView(entry: entry)
}
}
}
Deep Links from Widgets
با URL روی ویجت تپ می شود. سپس برنامه به صفحه هدف می رود.
import SwiftUI
struct NotesCountView: View {
var entry: CountEntry
var body: some View {
Link(destination: URL(string: "myapp://notes")!) {
Text("Notes: \(entry.count)")
}
}
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
// Route based on url
}
}
}
}
Refresh & Timeline Policy
راهکار تازه سازی را انتخاب کن. سپس در زمان مناسب رفرش کن.
import WidgetKit
// Update at a future date
completion(Timeline(entries: entries, policy: .after(Date().addingTimeInterval(1800))))
// Or end when entries are exhausted
// completion(Timeline(entries: entries, policy: .atEnd))
import WidgetKit
// Refresh a specific widget kind after writing new data to App Group
WidgetCenter.shared.reloadTimelines(ofKind: "NotesCount")
// Or refresh all widget kinds
// WidgetCenter.shared.reloadAllTimelines()
Supported Families
خانواده های پشتیبانی شده را اعلام کن. سپس اندازه ها را تست کن.
import WidgetKit
@main
struct NotesCountWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "NotesCount", provider: NotesProvider()) { entry in
NotesCountView(entry: entry)
}
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Testing Widgets
پیش نمایش ویجت را ببین. سپس روی دستگاه اضافه کن. تغییر App Group را بده و رفرش کن.
جمع بندی سریع
- App Groups را فعال کن.
- Provider، Entry و View بساز.
- تایم لاین و Policy را تنظیم کن.
- دیپ لینک را برای ناوبری بده.
- اندازه ها را روی دستگاه تست کن.