Learn how to implement a Twitter feed in SwiftUI without any API access credentials using SwiftUI and WebKit.
Published by Philipp Matthes 2 months, 3 weeks ago
Taking the fact that Twitter recently deprecated their native TwitterKit library, we needed to search for a new alternative for our OUTPUT.DD iOS App and came up with something that is really simple and straightforward. The basic idea is to use a WKWebView
, which loads the Twitter widget and displays the desired Twitter feed.
This tutorial was written for Swift Version: 5.3
Create a new file and name it TwitterViewTemplate.html
. Make sure it is included in the app’s bundle, since we are going to load it from there later.
<!-- This is an html template for displaying a Twitter feed using the twitter widget. --> <meta name='viewport' content='initial-scale=1'/> <a class="twitter-timeline" href="https://twitter.com/outputdd"></a> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
The template will be rendered in its own WKWebView
, to which we will get in a moment. The basic idea is that the Template loads the Twitter widgets JavaScript from the official Twitter server. Afterwards, the Twitter widget replaces our <a>
with an actual HTML feed, which has basically the same UI as you already know it from Twitter. Note that the Template updates itself automatically, which is very convenient!
Since SwiftUI doesn’t natively support the WKWebView
yet, we will create our own custom wrapper. Create a new file called TwitterHTMLView.swift
and insert the following code:
import SwiftUI import WebKit /// A state indicating the loading state of the WebView. enum TwitterHTMLViewState { case loading case idle } /// A simple navigation delegate for the twitter html view. /// /// The delegate enables the webview to open tweets in the default browser. fileprivate class TwitterHTMLViewNavigator: NSObject, WKNavigationDelegate { private let onStateChange: (TwitterHTMLViewState) -> Void init(onStateChange: @escaping (TwitterHTMLViewState) -> Void) { self.onStateChange = onStateChange } func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard navigationAction.navigationType == .linkActivated else { decisionHandler(.allow) return } guard let url = navigationAction.request.url, UIApplication.shared.canOpenURL(url) else { decisionHandler(.cancel) return } UIApplication.shared.open(url) decisionHandler(.allow) } func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { // This method is called when the WebView is loading. onStateChange(.loading) } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // This method is called when the WebView finished loading. onStateChange(.idle) } } /// A html view based on WKWebView which wraps a simple twitter feed. struct TwitterHTMLView: UIViewRepresentable { private let navigator: WKNavigationDelegate init(onStateChange: @escaping (TwitterHTMLViewState) -> Void) { self.navigator = TwitterHTMLViewNavigator( onStateChange: onStateChange ) } func makeUIView(context: Context) -> WKWebView { let view = WKWebView(frame: .zero) guard let htmlFilepath = Bundle.main.path( forResource: "TwitterViewTemplate", ofType: "html" ), let html = try? String(contentsOfFile: htmlFilepath) else { fatalError("Twitter Assets not found!") } view.loadHTMLString(html, baseURL: nil) view.navigationDelegate = navigator return view } func updateUIView( _ view: WKWebView, context: UIViewRepresentableContext<TwitterHTMLView> ) { /* Protocol requirement */} }
This SwiftUI HTML view now automatically loads our HTML template and the included Twitter widget, which loads the Twitter feed. We use a navigation delegate to get notified of three events:
onStateChange
handler.Now, to conclude, we create a TwitterView.swift
file to wrap our TwitterHTMLView
and react to any loading state changes. Also, we add a custom toolbar action for our navigation view to create a new Tweet.
import SwiftUI /// A twitter view which shows the output.dd twitter feed. struct TwitterView: View { @State private var isLoading = true private var newTweetURL: URL { // Create a new tweet with @OUTPUT.DD as text (urlencoded) let urlString = "https://twitter.com/intent/tweet?text=%40OUTPUT.DD" guard let url = URL(string: urlString) else { fatalError("Erroneous tweet url detected!") } return url } var body: some View { NavigationView { ZStack { TwitterHTMLView { loadingState in switch loadingState { case .idle: isLoading = false case .loading: isLoading = true } } if isLoading { VStack { Text("Loading Twitter Feed...") ProgressView() } } } .edgesIgnoringSafeArea(.vertical) .navigationBarTitle("Twitter") .toolbar { ToolbarItem(placement: .automatic) { Link(destination: newTweetURL, label: { HStack { Image(systemName: "plus") Text("Tweet") } }) } } } } }
This is how the finished TwitterView
looks like in Xcode:
You can download the views for free here:
Thank you for reading, if this did help you, give me a shoutout or buy me a coffee, if you want to 😎
swift swiftui twitter feed webkit tutorial
Published by Philipp Matthes 2 months ago
The FCRN neural network can be used to interpret depth maps on single-lens camera images. But how can we further improve the depth maps based on the intial photo?
Read Blog EntryPublished by Felix Kästner and Philipp Matthes 2 months, 3 weeks ago
Wir haben einen Vortrag für OUTPUT.DD eingereicht zum Thema "Microservices und Containerisierung im Cloud Computing"!
Read Blog Entry