Skip to content

Commit

Permalink
Update to 4.0.0
Browse files Browse the repository at this point in the history
Merge pull request #58 from sbertix/development
  • Loading branch information
sbertix authored Aug 17, 2020
2 parents c6f960c + 1596b61 commit 17a8996
Show file tree
Hide file tree
Showing 58 changed files with 3,241 additions and 969 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ jobs:
env:
DS_USER_ID: ${{ secrets.DS_USER_ID }}
RUR: ${{ secrets.RUR }}
MID: ${{ secrets.MID }}
SESSIONID: ${{ secrets.SESSIONID }}
CSRFTOKEN: ${{ secrets.CSRFTOKEN }}
MID: ${{ secrets.MID }}
run: swift test -v --enable-test-discovery --enable-code-coverage
- name: Coverage (1)
run: xcrun llvm-cov export -format="lcov" .build/debug/SwiftagramPackageTests.xctest/Contents/MacOS/SwiftagramPackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov
Expand Down
4 changes: 2 additions & 2 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ type_body_length:
error: 1000

function_body_length:
warning: 115
error: 120
warning: 180
error: 200

line_length:
warning: 150
Expand Down
8 changes: 2 additions & 6 deletions Examples/Followers/Followers.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
E028F0302418DFCC00F9C7F8 /* FollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E028F0212418DFCC00F9C7F8 /* FollowersView.swift */; };
E028F0312418DFCC00F9C7F8 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E028F0222418DFCC00F9C7F8 /* FollowersModel.swift */; };
E028F0332418DFCC00F9C7F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E028F0242418DFCC00F9C7F8 /* Assets.xcassets */; };
E028F0342418DFCC00F9C7F8 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = E028F0262418DFCC00F9C7F8 /* User.swift */; };
E028F0352418DFCC00F9C7F8 /* UserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E028F0272418DFCC00F9C7F8 /* UserCell.swift */; };
E028F0362418DFCC00F9C7F8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E028F0292418DFCC00F9C7F8 /* Preview Assets.xcassets */; };
E028F0372418DFCC00F9C7F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E028F02A2418DFCC00F9C7F8 /* LaunchScreen.storyboard */; };
Expand All @@ -28,7 +27,6 @@
E028F0212418DFCC00F9C7F8 /* FollowersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FollowersView.swift; sourceTree = "<group>"; };
E028F0222418DFCC00F9C7F8 /* FollowersModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
E028F0242418DFCC00F9C7F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E028F0262418DFCC00F9C7F8 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
E028F0272418DFCC00F9C7F8 /* UserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserCell.swift; sourceTree = "<group>"; };
E028F0292418DFCC00F9C7F8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
E028F02B2418DFCC00F9C7F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
Expand Down Expand Up @@ -99,7 +97,6 @@
E028F0252418DFCC00F9C7F8 /* User */ = {
isa = PBXGroup;
children = (
E028F0262418DFCC00F9C7F8 /* User.swift */,
E028F0272418DFCC00F9C7F8 /* UserCell.swift */,
);
path = User;
Expand Down Expand Up @@ -202,7 +199,6 @@
buildActionMask = 2147483647;
files = (
E028F0382418DFCC00F9C7F8 /* AppDelegate.swift in Sources */,
E028F0342418DFCC00F9C7F8 /* User.swift in Sources */,
E028F0392418DFCC00F9C7F8 /* ContentView.swift in Sources */,
E028F0302418DFCC00F9C7F8 /* FollowersView.swift in Sources */,
E028F0352418DFCC00F9C7F8 /* UserCell.swift in Sources */,
Expand Down Expand Up @@ -420,8 +416,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "[email protected]:sbertix/Swiftagram.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 3.0.0;
kind = upToNextMinorVersion;
minimumVersion = 4.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
4 changes: 3 additions & 1 deletion Examples/Followers/Followers/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import UIKit

import ComposableRequest
import ComposableRequestCrypto
import Swiftagram
import SwiftagramCrypto

@UIApplicationMain
Expand All @@ -17,7 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Delete keychain when reinstalling the app.
if !UserDefaults.standard.bool(forKey: "launched.before") {
KeychainStorage().removeAll()
ComposableRequestCrypto.KeychainStorage<Secret>().removeAll()
UserDefaults.standard.set(true, forKey: "launched.before")
UserDefaults.standard.synchronize()
}
Expand Down
77 changes: 31 additions & 46 deletions Examples/Followers/Followers/Followers/FollowersModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Combine
import Foundation

import ComposableRequestCrypto
import Swiftagram
import SwiftagramCrypto

Expand All @@ -26,71 +27,55 @@ final class FollowersModel: ObservableObject {
/// The logged in secret.
var secret: Secret? {
didSet {
guard let secret = secret, secret.identifier != oldValue?.identifier else { return }
guard let secret = secret, secret.id != oldValue?.id else { return }
fetch(secret: secret)
}
}

/// Cancellable for user's info.
var userCancellable: AnyCancellable?
/// Cancellable for followers.
var followersCancellable: AnyCancellable?
/// Subscriptions.
private var subscriptions: Set<AnyCancellable> = []

// MARK: Lifecycle
/// Init.
init() { start() }

/// Check for `Secret` in `KeychainStorage`.
/// - returns: `true` if it was started, `false` otherwise.
@discardableResult
func start() -> Bool {
// Check for `Secret` in `KeychainStorage`.
guard let secret = KeychainStorage().all().first else { return false }
self.secret = secret
self.current = UserDefaults.standard
.data(forKey: secret.identifier)
.flatMap { try? JSONDecoder().decode(User.self, from: $0) }
return true
init() {
// Fetch the current `Secret`.
if let secret = ComposableRequestCrypto.KeychainStorage<Secret>().all().first {
self.secret = secret
self.current = UserDefaults.standard
.data(forKey: secret.id)
.flatMap { try? JSONDecoder().decode(User.self, from: $0) }
// Fetch the data.
self.fetch(secret: secret)
}
// Keep `UserDefaults` in sync.
// This will only persist new `User`s, not delete old ones: this is just an example.
$current.compactMap { $0 }
.removeDuplicates(by: { $0.identifier == $1.identifier })
.map { (data: try? JSONEncoder().encode($0), id: $0.identifier) }
.sink { UserDefaults.standard.set($0.data, forKey: $0.id) }
.store(in: &subscriptions)
}

/// Fetch values.
func fetch(secret: Secret) {
// Load info for the logged in user.
userCancellable = Endpoint.User.summary(for: secret.identifier)
Endpoint.User.summary(for: secret.id)
.unlocking(with: secret)
.publish()
.map {
guard let username = $0.user.username.string() else { return nil }
return User(username: username,
name: $0.user.fullName.string(),
avatar: $0.user.profilePicUrl.url())
}
.handleEvents(receiveOutput: {
$0.flatMap { try? JSONEncoder().encode($0) }
.flatMap { UserDefaults.standard.set($0, forKey: secret.identifier) }
UserDefaults.standard.synchronize()
})
.map(\.user)
.catch { _ in Empty() }
.assign(to: \.current, on: self)
// Load the first set of followers.
.store(in: &subscriptions)
// Load the first 3 pages of the current user's followers.
// In a real app you might want to fetch all of them.
followers = []
followersCancellable = Endpoint.Friendship.following(secret.identifier)
Endpoint.Friendship.following(secret.id)
.unlocking(with: secret)
.publish()
.prefix(3)
.map {
$0.users
.array()?
.compactMap {
guard let username = $0.username.string() else { return nil }
return User(username: username,
name: $0.fullName.string().flatMap {
let name = $0.trimmingCharacters(in: .whitespacesAndNewlines)
return name.isEmpty ? nil : name
},
avatar: $0.profilePicUrl.url())
} ?? []
}
.catch { error -> Empty<[User], Never> in print(error); return Empty() }
.compactMap(\.users)
.catch { _ in Empty() }
.assign(to: \.appendFollowers, on: self)
.store(in: &subscriptions)
}
}
18 changes: 10 additions & 8 deletions Examples/Followers/Followers/Login/LoginView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SwiftUI
import UIKit
import WebKit

import ComposableRequestCrypto
import Swiftagram
import SwiftagramCrypto

Expand All @@ -27,15 +28,16 @@ class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Authenticate.
WebViewAuthenticator(storage: KeychainStorage()) { self.webView = $0 }
.authenticate { [weak self] in
switch $0 {
case .failure(let error): print(error.localizedDescription)
case .success(let secret):
self?.completion?(secret)
self?.dismiss(animated: true, completion: nil)
}
WebViewAuthenticator(storage: ComposableRequestCrypto.KeychainStorage<Secret>()) {
self.webView = $0
}.authenticate { [weak self] in
switch $0 {
case .failure(let error): print(error.localizedDescription)
case .success(let secret):
self?.completion?(secret)
self?.dismiss(animated: true, completion: nil)
}
}
}
}

Expand Down
19 changes: 0 additions & 19 deletions Examples/Followers/Followers/User/User.swift

This file was deleted.

13 changes: 8 additions & 5 deletions Examples/Followers/Followers/User/UserCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import SwiftUI

import FetchImage
import Swiftagram

/// A `struct` displaying a remote image.
struct RemoteImage: View {
Expand Down Expand Up @@ -46,8 +47,8 @@ struct UserCell: View {
var body: some View {
HStack {
// The image or a placeholder.
if user.avatar != nil {
user.avatar.flatMap {
if user.thumbnail != nil {
user.thumbnail.flatMap {
RemoteImage(url: $0, placeholder: UIImage(named: "placeholder") ?? .init())
.frame(width: 44, height: 44)
.mask(Circle())
Expand All @@ -62,9 +63,11 @@ struct UserCell: View {
// The username and name.
VStack(alignment: .leading) {
Text(user.username).font(.headline)
if user.name != nil {
user.name.flatMap(Text.init)?.font(.footnote).foregroundColor(.secondary)
}
user.name
.flatMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.flatMap(Text.init)?
.font(.footnote)
.foregroundColor(.secondary)
}
}
.padding(.vertical, 8)
Expand Down
5 changes: 2 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/evgenyneu/keychain-swift.git", .upToNextMinor(from: "19.0.0")),
.package(url: "https://github.com/sbertix/ComposableRequest", .upToNextMinor(from: "4.0.0")),
.package(url: "https://github.com/sbertix/ComposableRequest", .upToNextMinor(from: "4.1.0")),
.package(url: "https://github.com/sbertix/SwCrypt", .upToNextMinor(from: "5.1.0"))
],
targets: [
Expand All @@ -26,7 +25,7 @@ let package = Package(
),
.target(
name: "SwiftagramCrypto",
dependencies: ["Swiftagram", "SwCrypt", "KeychainSwift"]
dependencies: ["Swiftagram", "ComposableRequestCrypto", "SwCrypt"]
),
.testTarget(
name: "SwiftagramTests",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import ComposableRequest
/// A `protocol` describing a form of fetching `Secret`s.
public protocol Authenticator {
/// A `Storage` concrete type in which `Secret` are stored.
associatedtype Storage: Swiftagram.Storage
associatedtype Storage: ComposableRequest.Storage
/// An `Error` concrete type.
associatedtype Error: Swift.Error

Expand Down
21 changes: 2 additions & 19 deletions Sources/Swiftagram/Authentication/Secret/Secret.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public struct Secret: HeaderKey {
}

/// A `String` representing the logged in user identifier.
public var identifier: String! {
return cookies.first(where: { $0.name == "ds_user_id" })?.value
public var id: String {
return cookies.first(where: { $0.name == "ds_user_id" })!.value
}

/// An `HTTPCookie` holding reference to the cross site request forgery token.
Expand Down Expand Up @@ -61,21 +61,4 @@ public struct Secret: HeaderKey {
self.cookies = cookies.compactMap(CodableHTTPCookie.init)
self.device = device
}

/// Init from `Storage`.
/// - parameters:
/// - identifier: The `ds_user_id` cookie value.
/// - storage: A concrete-typed value conforming to the `Storage` protocol.
public static func stored<S: Storage>(with identifier: String, in storage: S) -> Secret? {
return storage.find(matching: identifier)
}

// MARK: Locker
/// Store in `storage`.
/// - parameter storage: A value conforming to the `Storage` protocol.
@discardableResult
public func store(in storage: Storage) -> Secret {
storage.store(self)
return self
}
}
4 changes: 3 additions & 1 deletion Sources/Swiftagram/Authentication/Visual/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import Foundation
import WebKit

import ComposableRequest

/// A `class` describing a self-navigating `WKWebView`.
@available(iOS 11, macOS 10.13, macCatalyst 13, *)
internal final class WebView: WKWebView, WKNavigationDelegate {
internal final class WebView<Storage: ComposableRequest.Storage>: WKWebView, WKNavigationDelegate where Storage.Key == Secret {
/// Any `Storage`.
internal var storage: Storage!
/// A block providing a `Secret`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import ComposableRequest
```
*/
@available(iOS 11.0, macOS 10.13, macCatalyst 13.0, *)
public final class WebViewAuthenticator<Storage: Swiftagram.Storage>: Authenticator {
public final class WebViewAuthenticator<Storage: ComposableRequest.Storage>: Authenticator where Storage.Key == Secret {
/// A `Storage` instance used to store `Secret`s.
public internal(set) var storage: Storage
/// A `UserAgent`.
Expand Down Expand Up @@ -90,7 +90,7 @@ public final class WebViewAuthenticator<Storage: Swiftagram.Storage>: Authentica
// Update the process pool.
let configuration = WKWebViewConfiguration()
configuration.processPool = WKProcessPool()
let webView = WebView(frame: .zero, configuration: configuration)
let webView = WebView<Storage>(frame: .zero, configuration: configuration)
webView.navigationDelegate = webView
webView.customUserAgent = self.userAgent.string
webView.storage = self.storage
Expand All @@ -109,7 +109,7 @@ public final class WebViewAuthenticator<Storage: Swiftagram.Storage>: Authentica

/// Extend for `TransientStorage`.
@available(iOS 11.0, macOS 10.13, macCatalyst 13.0, *)
public extension WebViewAuthenticator where Storage == TransientStorage {
public extension WebViewAuthenticator where Storage == ComposableRequest.TransientStorage<Secret> {
// MARK: Lifecycle
/// Init.
/// - parameter webView: A block outputing a configured `WKWebView`.
Expand Down
Loading

0 comments on commit 17a8996

Please sign in to comment.