How to workaround URLSession issue in watchOS 6.1.1

Issue #577 https://stackoverflow.com/questions/59724731/class-avassetdownloadtask-is-implemented-in-both-cfnetwork-and-avfoundation objc[45250]: Class AVAssetDownloadTask is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/watchOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CFNetwork.framework/CFNetwork (0x4ddd0ec) and /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/watchOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/AVFoundation.framework/AVFoundation (0x16aea494). One of the two will be used. Which one is undefined. objc[45250]: Class AVAssetDownloadURLSession is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/watchOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CFNetwork.framework/CFNetwork (0x4dddd44) and /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/watchOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/AVFoundation.framework/AVFoundation (0x16aea4bc). One of the two will be used. Which one is undefined. Then URLSession stops working. 2020-01-13 22:50:12.430920+0100 MyAppWatch WatchKit Extension[45250:2099229] Task <3CECDE81-59B9-4EDE-A4ED-1BA173646037>.<1> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://myapp.com/def.json, NSErrorFailingURLStringKey=https://myapp.com/def.json, NSLocalizedDescription=cancelled} The workaround is to remove Combine based API, and use completion block....

January 13, 2020 路 1 min 路 Khoa Pham

How to show list with section in SwiftUI

Issue #511 struct CountriesView: View { let groups: [Group] init(countries: [Country]) { self.groups = CountryManager.shared.groups(countries: countries) } var body: some View { List { ForEach(groups) { group in Section( header: Text(group.initial) .foregroundColor(Color.yellow) .styleTitle(), content: { ForEach(group.countries) { country in CountryRow(country: country) } } ) } } } } ...

November 16, 2019 路 1 min 路 Khoa Pham

How to make Swift Package Manager package for multiple platforms

Issue #504 https://twitter.com/NeoNacho/status/1181245484867801088?s=20 There鈥檚 no way to have platform specific sources or targets today, so you鈥檒l have to take a different approach. I would recommend wrapping all OS specific files in #if os and just having one target. For tests, you could do something similar, one test target, but conditional tests Every files are in Sources folder, so we can use platform and version checks. For example Omnia is a Swift Package Manager that supports iOS, tvOS, watchOS, macOS and Catalyst....

November 13, 2019 路 1 min 路 Khoa Pham

How to make multiline Text in SwiftUI in watchOS

Issue #482 lineLimit does not seem to work, use fixedSize instead Fixes this view at its ideal size. A view that fixes this view at its ideal size in the dimensions given in fixedDimensions. extension Text { func styleText() -> some View { return self .font(.footnote) .foregroundColor(.gray) .lineLimit(10) .fixedSize(horizontal: false, vertical: true) } } ...

October 31, 2019 路 1 min 路 Khoa Pham

How to use Swift package manager in watchOS

Issue #474 SPM Go to Project -> Swift Packages, add package. For example https://github.com/onmyway133/EasyStash Select your WatchKit Extension target, under Frameworks, Libraries and Embedded Content add the library CocoaPods If we use CocoaPods, then it needs to be in WatchKit Extension target 'MyApp WatchKit Extension' do use_frameworks! pod 'EasyStash', :git => 'https://github.com/onmyway133/EasyStash' end ...

October 23, 2019 路 1 min 路 Khoa Pham

How to reload data without using onAppear in SwiftUI in watchOS

Issue #468 From onAppeear Adds an action to perform when the view appears. In theory, this should be triggered every time this view appears. But in practice, it is only called when it is pushed on navigation stack, not when we return to it. So if user goes to a bookmark in a bookmark list, unbookmark an item and go back to the bookmark list, onAppear is not called again and the list is not updated....

October 17, 2019 路 2 min 路 Khoa Pham

How to use EnvironmentObject in SwiftUI for watchOS

Issue #467 Declare top dependencies in ExtensionDelegate class ExtensionDelegate: NSObject, WKExtensionDelegate { let storeContainer = StoreContainer() func applicationDidEnterBackground() { storeContainer.save() } } Reference that in HostingController. Note that we need to change from generic MainView to WKHostingController<AnyView> as environmentObject returns View protocol class HostingController: WKHostingController<AnyView> { var storeContainer: StoreContainer! override func awake(withContext context: Any?) { super.awake(withContext: context) self.storeContainer = (WKExtension.shared().delegate as! ExtensionDelegate).storeContainer } override var body: AnyView { return AnyView(MainView() ....

October 17, 2019 路 1 min 路 Khoa Pham

How to create watch only watchOS app

Issue #457 From Creating Independent watchOS Apps The root target is a stub, and acts as a wrapper for your project, so that you can submit it to the App Store. The other two are identical to the targets found in a traditional watchOS project. They represent your WatchKit app and WatchKit extension, respectively. ...

October 10, 2019 路 1 min 路 Khoa Pham

How to show web content as QR code in SwiftUI in watchOS

Issue #449 WatchKit does not have Web component, despite the fact that we can view web content https://www.imore.com/how-view-web-pages-apple-watch-watchos-5 A workaround is to show url as QR code import SwiftUI struct QRCodeView: View { let title: String let url: URL var body: some View { GeometryReader { geometry in VStack { self.makeImage(size: geometry.size) .padding(.top, 10) Text("Scan to open") .font(.system(.footnote)) }.navigationBarTitle(self.title) } } private func makeImage(size: CGSize) -> some View { let value = size....

October 8, 2019 路 1 min 路 Khoa Pham

How to load remote image in SwiftUI

Issue #448 Use ObservableObject and onReceive to receive event. URLSession.dataTask reports in background queue, so need to .receive(on: RunLoop.main) to receive events on main queue. For better dependency injection, need to use ImageLoader from Environment There should be a way to propagate event from Publisher to another Publisher, for now we use sink ImageLoader.swift import Combine import WatchKit class ImageLoader: ObservableObject { private var cancellable: AnyCancellable? let objectWillChange = PassthroughSubject<UIImage?, Never>() func load(url: URL) { self....

October 8, 2019 路 1 min 路 Khoa Pham

How to do navigation in SwiftUI in watchOS

Issue #447 NavigationView is not available on WatchKit, but we can just use NavigationLink List(services.map({ AnyService($0) })) { anyService in NavigationLink(destination: ItemsView(service: anyService.service) .navigationBarTitle(anyService.service.name) .onDisappear(perform: { anyService.service.requestCancellable?.cancel() }) ) { HStack { Image(anyService.service.name) .resizable() .frame(width: 30, height: 30, alignment: .leading) Text(anyService.service.name) } } } Adding NavigationLink to a View adds a round shadow cropping effect, which is usually not want we want. But we shouldn鈥檛 wrap Button as Button handles its own touch event, plus it has double shadow effect....

October 8, 2019 路 1 min 路 Khoa Pham