Skip to content

Commit 3453b4b

Browse files
committed
iOS app with basic list view
1 parent 81cdbd6 commit 3453b4b

File tree

20 files changed

+377
-198
lines changed

20 files changed

+377
-198
lines changed

Chapter 14/MyProjectApi/Sources/MyProjectApi/Api.swift

Lines changed: 0 additions & 32 deletions
This file was deleted.

Chapter 14/MyProjectApi/Sources/MyProjectApi/Blog/BlogPostObjects.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,4 @@ public struct BlogPostPatchObject: Codable {
7777
self.categoryId = categoryId
7878
}
7979
}
80+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "Default.jpg",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"idiom" : "universal",
10+
"scale" : "2x"
11+
},
12+
{
13+
"idiom" : "universal",
14+
"scale" : "3x"
15+
}
16+
],
17+
"info" : {
18+
"author" : "xcode",
19+
"version" : 1
20+
}
21+
}
362 KB
Loading

Chapter 14/MyProjectClient/iOS/Assets/Info.plist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,10 @@
5656
<string>UIInterfaceOrientationLandscapeLeft</string>
5757
<string>UIInterfaceOrientationLandscapeRight</string>
5858
</array>
59+
<key>NSAppTransportSecurity</key>
60+
<dict>
61+
<key>NSAllowsArbitraryLoads</key>
62+
<true/>
63+
</dict>
5964
</dict>
6065
</plist>

Chapter 14/MyProjectClient/iOS/Sources/App/Cells/CustomCell.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,33 @@ class CustomCell: UITableViewCell {
2929
coverView.translatesAutoresizingMaskIntoConstraints = false
3030
self.contentView.addSubview(coverView)
3131
self.coverView = coverView
32+
self.coverView.contentMode = .scaleAspectFill
33+
self.coverView.clipsToBounds = true
34+
self.coverView.layer.cornerRadius = 8
3235

3336
let titleLabel = UILabel(frame: .zero)
3437
titleLabel.translatesAutoresizingMaskIntoConstraints = false
3538
self.contentView.addSubview(titleLabel)
3639
self.titleLabel = titleLabel
40+
self.titleLabel.textAlignment = .center
3741

3842
NSLayoutConstraint.activate([
39-
self.contentView.topAnchor.constraint(equalTo: self.coverView.topAnchor),
40-
self.contentView.bottomAnchor.constraint(equalTo: self.coverView.bottomAnchor),
41-
self.contentView.leadingAnchor.constraint(equalTo: self.coverView.leadingAnchor),
42-
self.contentView.trailingAnchor.constraint(equalTo: self.coverView.trailingAnchor),
43-
44-
self.contentView.centerXAnchor.constraint(equalTo: self.titleLabel.centerXAnchor),
45-
self.contentView.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor),
43+
self.coverView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 16),
44+
self.coverView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 16),
45+
self.coverView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -16),
46+
self.coverView.bottomAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 0),
47+
48+
self.titleLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 16),
49+
self.titleLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -16),
50+
self.titleLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: 0),
51+
self.titleLabel.heightAnchor.constraint(equalToConstant: 44),
4652
])
47-
48-
self.titleLabel.font = UIFont.systemFont(ofSize: 64)
4953
}
5054

5155
override func prepareForReuse() {
5256
super.prepareForReuse()
5357

58+
self.titleLabel.text = nil
5459
self.coverView.image = nil
5560
}
5661
}

Chapter 14/MyProjectClient/iOS/Sources/App/Cells/MyCell.swift

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// URLSession+DowloadTaskPublisher.swift
3+
// MyProject
4+
//
5+
// Created by Tibor Bodecs on 2020. 05. 17..
6+
// Copyright © 2020. Tibor Bodecs. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import Combine
11+
12+
extension URLSession {
13+
14+
public func downloadTaskPublisher(for url: URL) -> URLSession.DownloadTaskPublisher {
15+
self.downloadTaskPublisher(for: .init(url: url))
16+
}
17+
18+
public func downloadTaskPublisher(for request: URLRequest) -> URLSession.DownloadTaskPublisher {
19+
.init(request: request, session: self)
20+
}
21+
22+
public struct DownloadTaskPublisher: Publisher {
23+
24+
public typealias Output = (url: URL, response: URLResponse)
25+
public typealias Failure = URLError
26+
27+
public let request: URLRequest
28+
public let session: URLSession
29+
30+
public init(request: URLRequest, session: URLSession) {
31+
self.request = request
32+
self.session = session
33+
}
34+
35+
public func receive<S>(subscriber: S) where S: Subscriber,
36+
DownloadTaskPublisher.Failure == S.Failure,
37+
DownloadTaskPublisher.Output == S.Input
38+
{
39+
let subscription = DownloadTaskSubscription(subscriber: subscriber, session: self.session, request: self.request)
40+
subscriber.receive(subscription: subscription)
41+
}
42+
}
43+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// URLSession+DownloadTaskSubscription.swift
3+
// MyProject
4+
//
5+
// Created by Tibor Bodecs on 2020. 05. 17..
6+
// Copyright © 2020. Tibor Bodecs. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import Combine
11+
12+
extension URLSession {
13+
14+
final class DownloadTaskSubscription<SubscriberType: Subscriber>: Subscription where
15+
SubscriberType.Input == (url: URL, response: URLResponse),
16+
SubscriberType.Failure == URLError
17+
{
18+
private var subscriber: SubscriberType?
19+
private weak var session: URLSession!
20+
private var request: URLRequest!
21+
private var task: URLSessionDownloadTask!
22+
23+
init(subscriber: SubscriberType, session: URLSession, request: URLRequest) {
24+
self.subscriber = subscriber
25+
self.session = session
26+
self.request = request
27+
}
28+
29+
func request(_ demand: Subscribers.Demand) {
30+
guard demand > 0 else {
31+
return
32+
}
33+
self.task = self.session.downloadTask(with: request) { [weak self] url, response, error in
34+
if let error = error as? URLError {
35+
self?.subscriber?.receive(completion: .failure(error))
36+
return
37+
}
38+
guard let response = response else {
39+
self?.subscriber?.receive(completion: .failure(URLError(.badServerResponse)))
40+
return
41+
}
42+
guard let url = url else {
43+
self?.subscriber?.receive(completion: .failure(URLError(.badURL)))
44+
return
45+
}
46+
do {
47+
let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
48+
let fileUrl = cacheDir.appendingPathComponent((UUID().uuidString))
49+
try FileManager.default.moveItem(atPath: url.path, toPath: fileUrl.path)
50+
_ = self?.subscriber?.receive((url: fileUrl, response: response))
51+
self?.subscriber?.receive(completion: .finished)
52+
}
53+
catch {
54+
self?.subscriber?.receive(completion: .failure(URLError(.cannotCreateFile)))
55+
}
56+
}
57+
self.task.resume()
58+
}
59+
60+
func cancel() {
61+
self.task.cancel()
62+
}
63+
}
64+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// RootEntity.swift
3+
// MyProject
4+
//
5+
// Created by Tibor Bodecs on 2020. 05. 16..
6+
// Copyright © 2020. Tibor Bodecs. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct RootEntity {
12+
13+
struct Item {
14+
let id: UUID
15+
let title: String
16+
let imageUrl: String
17+
let url: String
18+
}
19+
20+
let items: [Item]
21+
}

0 commit comments

Comments
 (0)