Search

[실험실] SwiftUI - Widget 사용해보기

제 위젯위젯이 응답을 하지 않아서 약간쓰.. 눈물날 뻔..
이번주도 뒹굴거려서 늦게나마 위젯 열차에 탑승했고, 제 열차는 브레이크가 없어서 생사의 기로에 왔다감 ^^! 와!
저는 정말 제가 해야하는 것만 했어요 .. . ^^*
QR코드는 위젯이 하나밖에 없잖아효? 그래서 그것만 했습니다 .. . ^^*
선배들은 뭐 그렇게 많이 한 거 ㅇ ㅑ..?

SwiftUI

일단 다른 분들이랑 비슷하게 코드를 짰습니다.
struct QRWidgetEntryView : View { var entry: Provider.Entry private static let deeplinkURL: URL = URL(string: "widget://qrcode")! var body: some View { ZStack { Color.init(UIColor.init(red: 249/255, green: 224/255, blue: 7/255, alpha: 1.0)) Image("WidgetBackground") .resizable() .frame(width: 145, height: 130, alignment: .center) } .widgetURL(QRWidgetEntryView.deeplinkURL) } } @main struct QRWidget: Widget { let kind: String = "QRWidget" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in QRWidgetEntryView(entry: entry) } .configurationDisplayName("Kakao QR Widget") .description("클론코딩을 통한 Kakao QR 위젯입니다.") .supportedFamilies([.systemSmall]) } }
Swift
복사
크게 다를 바는 없고 저는 딱 위젯만 만들어서 .systemSmall 사이즈 하나만 넣어줬어요.
body에는 일단 ZStack으로 색깔과 Image를 넣어줬고,
꿀팁일지는 모르겠지만 Color로 UIColor를 SwiftUI에서는 쓸 수 없어요 . . 그래서 저렇게 init을 사용해서 UIColor를 넣어줘야 합니다..

Deeplink

앱에 뎁스를 줘서 열기 위한 고군분투가 시작됐습니다. 안 열려요.
첫번째 도전
Link를 사용했습니다.
그 분은 아쉽게도 탈락을 했습니다.
탈락사유 : 일단 밑에 있는 이 링크의 블로그님의 예제를 보고 똑같이 따라했지만 저는 뎁스가 있게 열 수 없었습니다. 왜냐면 LinkMedium Large 크기의 위젯에서만 사용 가능하다고 합니다. small의 경우는 안된다고 하네요 . .. 분명 하면서 봤는데 출처를 찾아올 수 없습니다. 제 꿈 속 일지도
두번째 도전
widgetURL를 사용했습니다.
이 분으로 성공했습니다.
저어..는 일단 현규 교수님의 블로그의 도움을 받았구요.
그 다음부터 안에 있는 코드들을 Reactor에 맞게 바꿔줬습니다.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let qrcodeLinkPath = "" guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true), let path = components.path else { return false } if path == qrcodeLinkPath { var nextVC = QRCodeVC() nextVC.bind(reactor: QRCodeReactor(root: nextVC)) nextVC.modalPresentationStyle = .overFullScreen if var rootVC = window?.rootViewController as? MainVC { rootVC.useWidget = true rootVC.bind(reactor: MainReactor()) } return true } else { return false } }
Swift
복사
AppDelegate에서는 이런식으로 쓰는데 만약에 SceneDelegate를 쓴다면
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { if let url = URLContexts.first?.url { if url.absoluteString.starts(with: "widget://pr") { guard let urlComponents = URLComponents(string: url.absoluteString) else { return } guard let pullRequestUrl = urlComponents.queryItems?.first(where: { $0.name == "url" })?.value else { return } let prDetailViewController = PRDetailViewController(urlString: pullRequestUrl) (self.window?.rootViewController as? UINavigationController)?.pushViewController(prDetailViewController, animated: true) } }
Swift
복사
요런 형식을 써주시면 됩니다.
url이라고 써있는 매개변수들로 우리가 넣어준 url들이 들어오면서 해당 함수가 쇽샥 돌아갑니다.
그리고 안에 있는 코드들을 실행시켜줄거예요. 든든하죠. 든든댄스 든든댄스.

Reactor

솔직히 저번주에 뷰를 짜면서 어느정도 Reactor 세팅을 마쳤더니 이번에는 크게 세팅해줄 점은 없었습니다.
이번에는 AppDelegate에서 저런 식으로 뷰를 열어줬을 때(위젯 사용),
Reactor를 bind해주지 않으면 Present가 안되길래 애를 좀 먹었고, 안에다가 Present하는 코드를 짜는 것도 애먹었습니다.. . . eat 애 many many . .
func bindReactor() { if useWidget { Observable.just("") .map { _ in Reactor.Action.shake(vc: self) } .bind(to: reactor.action) .disposed(by: bag) } }
Swift
복사
이런식으로 useWidget이 true이면, 저 코드를 실행할건데요.
just 자체가 들어오는 단 하나의 Observable만 바로바로 처리하는 애라서 아모튼 저렇게 map에 있는 shake(QR열어주는 코드)를 실행하도록 했습니다.
useWidget은 위에 AppDelegate 코드를 보면 알겠지만, 저 부분에서 들어가는거라면 true가 되도록 했습니다.