앱 확장 프로그램을 사용하면 맞춤형 기능과 콘텐츠를 앱 외부로 확장할 수 있으며 사용자가 다른 앱 또는 시스템과 상호 작용하는 동안에도 앱 기능을 사용할 수 있습니다.
it implements a specific, well scoped task that adheres to the policies defined by a particular extension point.
호스트앱에서 다른 앱의 기능을 가져오는 것
extension과 연결되어있는 본체 앱: ex: google photo
An app extension communicates primarily with its host app, and does so in terms reminiscent of transaction processing
*트랜잭션은 작업의 완전성을 보장해주는 것입니다. 즉, 논리적인 작업 set을 모두 완벽하게 처리하거나 또는 처리하지 못할 경우에는 원 상태로 복구해서 작업의 일부만 적용되는 현상이 발생하지 않게 만들어주는 기능입니다. 사용자의 입장에서는 작업의 논리적 단위로 이해할 수 있고 시스템 입장에서는 데이터를 접근 또는 변경하는 프로그램의 단위가 됩니다.
Because an app extension is not an app, its life cycle and environment are different. An app that a user employs to choose an app extension is called host
import Foundation
var landmarks: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
var landmarks: [Landmark] = load("landmarkData.json")
@main
struct LandmarksWidget: Widget {
let kind: String = "LandmarksWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
LandmarksWidgetEntryView(entry: entry)
}
.configurationDisplayName("LandmarksWidget")
.description("세계 곳곳의 랜드마크 사진을 확인하세요")
}
}
struct LandmarksEntry: TimelineEntry {
let date: Date
let number: Int
}
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> LandmarksEntry {
LandmarksEntry(date: Date(), number: 2)
}
func getSnapshot(in context: Context, completion: @escaping (LandmarksEntry) -> ()) {
let entry = LandmarksEntry(date: Date(), number: 2)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [LandmarksEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for minuteOffset in 0 ..< 10 {
let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset, to: currentDate)!
let entry = LandmarksEntry(date: entryDate, number: minuteOffset)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
placeholder: widget이 렌더링 되기 전에 나타날 화면을 전달하는 메서드
getSnapshot: widget gallery에서 어떤 위젯 화면이 보여질지 전달하는 메서드
getTimeLine: timeline 객체 속에 언제 무엇을 업데이트해야하는지 알려주는 entry들을 넣어줌. currentDate에 1분씩 더해진 형태로 시간과 정보를 저장합니다.
policy: .atEnd
, .after(_:)
, never
atEnd
: 정해진 timeLine의 모든 시간 정책이 끝난 뒤 reload timeline 해줌
.after(2 hr)
: 정해진 timeLine의 시간 정책을 다쓰기 전에 2시간이 지난 뒤 timeline을 reload
struct LandmarkView: View {
var landmark: Landmark
var body: some View {
landmark.image
.resizable()
.aspectRatio(contentMode: .fill)
}
}
struct LandmarksWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
LandmarkView(landmark: landmarks[entry.number])
}
}
개발자 계정을 갖게 되면 userdefault와 keychain을 활용하여 앱 정보를 가져와서 위젯에 표현 해 줘보면 좋겠다. intent configuration을 통해서 user default에 정보를 작성하는 작업을 해 보면 좋겠다.