I was reading through the facebook react native tutorial and wondered how much more code it would take to create the example with UIKit. Obviously this code would only work on iOS, not other platforms supported by React Native. Its also a bit longer than the example Javascript code, but doesn't rely on the React Native framework, so in that respect it's much shorter.
You can download the example Xcode playground here, or view as a gist here. Please excuse the forced unwrapping, the code is just a quick-and-dirty example.
import UIKit
import PlaygroundSupport
let cellIdentifier = "cellId"
let dataUrl = URL(string: "https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json")!
func contstraint(_ attr: NSLayoutAttribute, from: UIView, to: UIView) -> NSLayoutConstraint {
return NSLayoutConstraint(
item: from,
attribute: attr,
relatedBy: .equal,
toItem: to,
attribute: attr,
multiplier: 1,
constant: 1
)
}
struct Movie {
let title: String
let thumbnail: URL
init?(json: [String : Any]) {
guard let title = json["title"] as? String,
let images = json["posters"] as? [String : String],
let tumbnailURLString = images["thumbnail"],
let thumbnailURL = URL(string: tumbnailURLString)
else {
return nil
}
self.title = title
self.thumbnail = thumbnailURL
}
}
func loadMovies(completionHandler: @escaping ([Movie]) -> Void) {
let task = URLSession(configuration: .default).dataTask(with: dataUrl) { (data, response, error) in
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any],
let movies = json["movies"] as? [[String : Any]]
{
completionHandler(movies.flatMap { Movie(json: $0) })
}
} catch {}
}
}
task.resume()
}
extension UIImageView {
func loadImage(url: URL, placeholder: String) {
image = UIImage(named: placeholder)
_ = URLSession(configuration: .default).dataTask(with: url) { [weak self] (data, response, error) in
if let data = data, let img = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = img
}
} else if let err = error {
print("Error: \(url) \(err)")
} else {
print("Error: \(url)")
}
}.resume()
}
}
class MovieCell: UITableViewCell {
init() {
super.init(style: .default, reuseIdentifier: cellIdentifier)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
class MoviesListViewController: UITableViewController {
var data: [Movie]
init(movies: [Movie]) {
data = movies
super.init(style: .plain)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = makeCell(tableView)
cell.textLabel?.text = data[indexPath.row].title
cell.imageView?.loadImage(url: data[indexPath.row].thumbnail, placeholder: "placeholder")
return cell
}
func makeCell(_ tableView: UITableView) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) else {
return MovieCell()
}
return cell
}
}
class LoadingView: UIView {
var loadingLabel: UILabel = {
let label = UILabel()
label.text = "Loading..."
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
addSubview(loadingLabel)
addConstraints([
contstraint(.centerX, from: loadingLabel, to: self),
contstraint(.centerY, from: loadingLabel, to: self),
])
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
class RootViewController: UIViewController {
var movieListVC: MoviesListViewController! = nil
override func viewDidLoad() {
super.viewDidLoad()
view = LoadingView(frame: view.frame)
view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
}
func loadMoviesAndPresent() {
loadMovies() { [weak self] movies in
guard self != nil else {
return
}
print("Loaded \(movies.count) movies")
self!.movieListVC = MoviesListViewController(movies: movies)
self!.movieListVC.modalTransitionStyle = .crossDissolve
self!.show(self!.movieListVC, sender: self)
}
}
}
let rootVC = RootViewController(nibName: nil, bundle: nil)
rootVC.loadMoviesAndPresent()
PlaygroundPage.current.liveView = rootVC