This commit is contained in:
Justin Mazzocchi 2020-08-31 11:57:02 -07:00
parent 5f96f59ac3
commit 2ca0dfe251
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
47 changed files with 120 additions and 116 deletions

View file

@ -7,7 +7,8 @@ import Stubbing
extension AccountEndpoint: Stubbing {
public func data(url: URL) -> Data? {
switch self {
case .verifyCredentials: return try? Data(contentsOf: Bundle.module.url(forResource: "account", withExtension: "json")!)
case .verifyCredentials: return try? Data(contentsOf: Bundle.module.url(forResource: "account",
withExtension: "json")!)
}
}
}

View file

@ -12,13 +12,12 @@
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
D01F41DF24F8868800D55A2D /* AttachmentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41DE24F8868800D55A2D /* AttachmentViewModel.swift */; };
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
D04FD74224D4AA34007D572D /* DevelopmentModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04FD74124D4AA34007D572D /* DevelopmentModels.swift */; };
D04FD74224D4AA34007D572D /* PreviewMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04FD74124D4AA34007D572D /* PreviewMocks.swift */; };
D052BBC724D749C800A80A7A /* RootViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D052BBC624D749C800A80A7A /* RootViewModelTests.swift */; };
D065F53924D37E5100741304 /* CombineExpectations in Frameworks */ = {isa = PBXBuildFile; productRef = D065F53824D37E5100741304 /* CombineExpectations */; };
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06B492224D4611300642749 /* KingfisherSwiftUI */; };
D075C28524FCD41D00D35112 /* Services in Frameworks */ = {isa = PBXBuildFile; productRef = D075C28424FCD41D00D35112 /* Services */; };
D075C28724FCD92400D35112 /* Services in Frameworks */ = {isa = PBXBuildFile; productRef = D075C28624FCD92400D35112 /* Services */; };
D0ADCBF124FD05510062ACCE /* ServiceMocks in Frameworks */ = {isa = PBXBuildFile; productRef = D0ADCBF024FD05510062ACCE /* ServiceMocks */; };
D0BDF66724FD7CDA00C7FA1C /* ServiceLayer in Frameworks */ = {isa = PBXBuildFile; productRef = D0BDF66624FD7CDA00C7FA1C /* ServiceLayer */; };
D0BDF66B24FD7CEC00C7FA1C /* ServiceLayer in Frameworks */ = {isa = PBXBuildFile; productRef = D0BDF66A24FD7CEC00C7FA1C /* ServiceLayer */; };
D0BEB1F324F8EE8C001B0F04 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */; };
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */; };
D0BEB1FD24F9E4E5001B0F04 /* ListsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1FC24F9E4E5001B0F04 /* ListsViewModel.swift */; };
@ -62,6 +61,7 @@
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46F24F76169001EBDBB /* View+Extensions.swift */; };
D0C7D4DB24F7616A001EBDBB /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D47024F76169001EBDBB /* Date+Extensions.swift */; };
D0C7D4DC24F7616A001EBDBB /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D47124F76169001EBDBB /* Data+Extensions.swift */; };
D0E2C1CE24FD7EE900854680 /* ServiceLayerMocks in Frameworks */ = {isa = PBXBuildFile; productRef = D0E2C1CD24FD7EE900854680 /* ServiceLayerMocks */; };
D0E5361C24E3EB4D00FB1CE1 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */; };
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D0ED1B6E24CE100C00B4899C /* AddIdentityViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1B6D24CE100C00B4899C /* AddIdentityViewModelTests.swift */; };
@ -105,11 +105,11 @@
D01F41DE24F8868800D55A2D /* AttachmentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentViewModel.swift; sourceTree = "<group>"; };
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
D04FD74124D4AA34007D572D /* DevelopmentModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevelopmentModels.swift; sourceTree = "<group>"; };
D04FD74124D4AA34007D572D /* PreviewMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewMocks.swift; sourceTree = "<group>"; };
D052BBC624D749C800A80A7A /* RootViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewModelTests.swift; sourceTree = "<group>"; };
D0666A2124C677B400F3F04B /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D0666A2524C677B400F3F04B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D075C28324FCD27300D35112 /* Services */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Services; sourceTree = "<group>"; };
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingTableFooterView.swift; sourceTree = "<group>"; };
D0BEB1FC24F9E4E5001B0F04 /* ListsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListsViewModel.swift; sourceTree = "<group>"; };
@ -169,9 +169,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D075C28524FCD41D00D35112 /* Services in Frameworks */,
D0ADCBF124FD05510062ACCE /* ServiceMocks in Frameworks */,
D0BDF66724FD7CDA00C7FA1C /* ServiceLayer in Frameworks */,
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */,
D0E2C1CE24FD7EE900854680 /* ServiceLayerMocks in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -187,7 +187,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D075C28724FCD92400D35112 /* Services in Frameworks */,
D0BDF66B24FD7CEC00C7FA1C /* ServiceLayer in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -217,7 +217,6 @@
isa = PBXGroup;
children = (
D0C7D45224F76169001EBDBB /* Assets.xcassets */,
D0ED1BB224CE3A1600B4899C /* Development Assets */,
D0C7D46824F76169001EBDBB /* Extensions */,
D0666A7924C7745A00F3F04B /* Frameworks */,
D0BFDAF524FC7C5300C86618 /* HTTP */,
@ -225,8 +224,9 @@
D0E0F1E424FC49FC002C04BF /* Mastodon */,
D0C7D43824F76169001EBDBB /* Model */,
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */,
D0ED1BB224CE3A1600B4899C /* Preview */,
D047FA8D24C3E21200AF17C5 /* Products */,
D075C28324FCD27300D35112 /* Services */,
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */,
D0C7D41D24F76169001EBDBB /* Supporting Files */,
D0C7D45324F76169001EBDBB /* System */,
D0666A2224C677B400F3F04B /* Tests */,
@ -383,12 +383,12 @@
path = "View Models";
sourceTree = "<group>";
};
D0ED1BB224CE3A1600B4899C /* Development Assets */ = {
D0ED1BB224CE3A1600B4899C /* Preview */ = {
isa = PBXGroup;
children = (
D04FD74124D4AA34007D572D /* DevelopmentModels.swift */,
D04FD74124D4AA34007D572D /* PreviewMocks.swift */,
);
path = "Development Assets";
path = Preview;
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -412,8 +412,8 @@
name = Metatext;
packageProductDependencies = (
D06B492224D4611300642749 /* KingfisherSwiftUI */,
D075C28424FCD41D00D35112 /* Services */,
D0ADCBF024FD05510062ACCE /* ServiceMocks */,
D0BDF66624FD7CDA00C7FA1C /* ServiceLayer */,
D0E2C1CD24FD7EE900854680 /* ServiceLayerMocks */,
);
productName = "Metatext (iOS)";
productReference = D047FA8C24C3E21200AF17C5 /* Metatext.app */;
@ -454,7 +454,7 @@
);
name = "Notification Service Extension";
packageProductDependencies = (
D075C28624FCD92400D35112 /* Services */,
D0BDF66A24FD7CEC00C7FA1C /* ServiceLayer */,
);
productName = "Notification Service Extension";
productReference = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */;
@ -586,7 +586,7 @@
D0C7D4CF24F7616A001EBDBB /* StatusViewModel.swift in Sources */,
D0C7D4C724F7616A001EBDBB /* PostingReadingPreferencesViewModel.swift in Sources */,
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */,
D04FD74224D4AA34007D572D /* DevelopmentModels.swift in Sources */,
D04FD74224D4AA34007D572D /* PreviewMocks.swift in Sources */,
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */,
D0C7D4DC24F7616A001EBDBB /* Data+Extensions.swift in Sources */,
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */,
@ -760,7 +760,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Supporting Files/Metatext.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "Development\\ Assets";
DEVELOPMENT_ASSET_PATHS = Preview;
DEVELOPMENT_TEAM = 82HL67AXQ2;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = "Supporting Files/Info.plist";
@ -786,7 +786,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Supporting Files/Metatext.entitlements";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "Development\\ Assets";
DEVELOPMENT_ASSET_PATHS = Preview;
DEVELOPMENT_TEAM = 82HL67AXQ2;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = "Supporting Files/Info.plist";
@ -971,17 +971,17 @@
package = D06B492124D4611300642749 /* XCRemoteSwiftPackageReference "Kingfisher" */;
productName = KingfisherSwiftUI;
};
D075C28424FCD41D00D35112 /* Services */ = {
D0BDF66624FD7CDA00C7FA1C /* ServiceLayer */ = {
isa = XCSwiftPackageProductDependency;
productName = Services;
productName = ServiceLayer;
};
D075C28624FCD92400D35112 /* Services */ = {
D0BDF66A24FD7CEC00C7FA1C /* ServiceLayer */ = {
isa = XCSwiftPackageProductDependency;
productName = Services;
productName = ServiceLayer;
};
D0ADCBF024FD05510062ACCE /* ServiceMocks */ = {
D0E2C1CD24FD7EE900854680 /* ServiceLayerMocks */ = {
isa = XCSwiftPackageProductDependency;
productName = ServiceMocks;
productName = ServiceLayerMocks;
};
/* End XCSwiftPackageProductDependency section */
};

View file

@ -3,7 +3,7 @@
import UserNotifications
import CryptoKit
import Mastodon
import Services
import ServiceLayer
class NotificationService: UNNotificationServiceExtension {

View file

@ -4,8 +4,9 @@ import Foundation
import Combine
import HTTP
import Mastodon
import Services
import ServiceMocks
import MastodonStubs
import ServiceLayer
import ServiceLayerMocks
// swiftlint:disable force_try
private let decoder = APIDecoder()
@ -15,11 +16,13 @@ private let devIdentityID = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E
private let devAccessToken = "DEVELOPMENT_ACCESS_TOKEN"
extension Account {
static let development = try! decoder.decode(Account.self, from: Data(officialAccountJSON.utf8))
static let development = try! decoder.decode(Account.self,
from: AccountEndpoint.verifyCredentials.data(url: devInstanceURL)!)
}
extension Instance {
static let development = try! decoder.decode(Instance.self, from: Data(officialInstanceJSON.utf8))
static let development = try! decoder.decode(Instance.self,
from: InstanceEndpoint.instance.data(url: devInstanceURL)!)
}
extension AppEnvironment {

View file

@ -3,18 +3,18 @@
import PackageDescription
let package = Package(
name: "Services",
name: "ServiceLayer",
platforms: [
.iOS(.v14),
.macOS(.v11)
],
products: [
.library(
name: "Services",
targets: ["Services"]),
name: "ServiceLayer",
targets: ["ServiceLayer"]),
.library(
name: "ServiceMocks",
targets: ["ServiceMocks"])
name: "ServiceLayerMocks",
targets: ["ServiceLayerMocks"])
],
dependencies: [
.package(url: "https://github.com/groue/CombineExpectations.git", .upToNextMajor(from: "0.5.0")),
@ -23,13 +23,13 @@ let package = Package(
],
targets: [
.target(
name: "Services",
name: "ServiceLayer",
dependencies: ["GRDB", "Mastodon"]),
.target(
name: "ServiceMocks",
dependencies: ["Services", .product(name: "MastodonStubs", package: "Mastodon")]),
name: "ServiceLayerMocks",
dependencies: ["ServiceLayer", .product(name: "MastodonStubs", package: "Mastodon")]),
.testTarget(
name: "ServicesTests",
dependencies: ["ServiceMocks", "CombineExpectations"])
name: "ServiceLayerTests",
dependencies: ["CombineExpectations", "ServiceLayerMocks"])
]
)

View file

@ -1,9 +1,9 @@
import Foundation
import HTTP
import Services
import ServiceLayer
import Stubbing
extension AppEnvironment {
public extension AppEnvironment {
static let mock = AppEnvironment(
session: Session(configuration: .stubbing),
webAuthSessionType: SuccessfulMockWebAuthSession.self,

View file

@ -0,0 +1,42 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import ServiceLayer
public struct MockKeychainService {}
public extension MockKeychainService {
static func reset() {
items = [String: Data]()
}
}
extension MockKeychainService: KeychainService {
public static func setGenericPassword(data: Data, forAccount key: String, service: String) throws {
items[key] = data
}
public static func deleteGenericPassword(account: String, service: String) throws {
items[account] = nil
}
public static func getGenericPassword(account: String, service: String) throws -> Data? {
items[account]
}
public static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data {
fatalError("not implemented")
}
public static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
fatalError("not implemented")
}
public static func deleteKey(applicationTag: String) throws {
fatalError("not implemented")
}
}
private extension MockKeychainService {
static var items = [String: Data]()
}

View file

@ -2,12 +2,12 @@
import Foundation
class MockUserDefaults: UserDefaults {
convenience init() {
public class MockUserDefaults: UserDefaults {
public convenience init() {
self.init(suiteName: Self.suiteName)!
}
override init?(suiteName suitename: String?) {
public override init?(suiteName suitename: String?) {
guard let suitename = suitename else { return nil }
UserDefaults().removePersistentDomain(forName: suitename)
@ -17,5 +17,5 @@ class MockUserDefaults: UserDefaults {
}
private extension MockUserDefaults {
private static let suiteName = "com.metatext.metabolist.mock-user-defaults"
private static let suiteName = "com.metabolist.metatext.mock-user-defaults"
}

View file

@ -1,15 +1,15 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import Services
import ServiceLayer
class MockWebAuthSession: WebAuthSession {
public class MockWebAuthSession: WebAuthSession {
let completionHandler: WebAuthSessionCompletionHandler
let url: URL
let callbackURLScheme: String?
var presentationContextProvider: WebAuthPresentationContextProviding?
public var presentationContextProvider: WebAuthPresentationContextProviding?
required init(
public required init(
url URL: URL,
callbackURLScheme: String?,
completionHandler: @escaping WebAuthSessionCompletionHandler) {
@ -18,7 +18,7 @@ class MockWebAuthSession: WebAuthSession {
self.completionHandler = completionHandler
}
func start() -> Bool {
public func start() -> Bool {
completionHandler(completionHandlerURL, completionHandlerError)
return true
@ -33,7 +33,7 @@ class MockWebAuthSession: WebAuthSession {
}
}
class SuccessfulMockWebAuthSession: MockWebAuthSession {
public class SuccessfulMockWebAuthSession: MockWebAuthSession {
private let redirectURL: URL
required init(
@ -60,7 +60,7 @@ class SuccessfulMockWebAuthSession: MockWebAuthSession {
}
}
class CanceledLoginMockWebAuthSession: MockWebAuthSession {
public class CanceledLoginMockWebAuthSession: MockWebAuthSession {
override var completionHandlerError: Error? {
WebAuthSessionError(.canceledLogin)
}

View file

@ -3,8 +3,8 @@
import XCTest
import Combine
import CombineExpectations
@testable import Services
@testable import ServiceMocks
@testable import ServiceLayer
@testable import ServiceLayerMocks
class AuthenticationServiceTests: XCTestCase {
func testAuthentication() throws {

View file

@ -1,42 +0,0 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import Services
struct MockKeychainService {}
extension MockKeychainService {
static func reset() {
items = [String: Data]()
}
}
extension MockKeychainService: KeychainService {
static func setGenericPassword(data: Data, forAccount key: String, service: String) throws {
items[key] = data
}
static func deleteGenericPassword(account: String, service: String) throws {
items[account] = nil
}
static func getGenericPassword(account: String, service: String) throws -> Data? {
items[account]
}
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data {
fatalError("not implemented")
}
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
fatalError("not implemented")
}
static func deleteKey(applicationTag: String) throws {
fatalError("not implemented")
}
}
private extension MockKeychainService {
static var items = [String: Data]()
}

View file

@ -1,7 +1,7 @@
// Copyright © 2020 Metabolist. All rights reserved.
import SwiftUI
import Services
import ServiceLayer
@main
struct MetatextApp: App {

View file

@ -7,7 +7,7 @@ import HTTP
import Mastodon
@testable import Metatext
import Services
import ServiceLayer
class AddIdentityViewModelTests: XCTestCase {
func testAddIdentity() throws {

View file

@ -3,7 +3,7 @@
import XCTest
import Combine
import CombineExpectations
import Services
import ServiceLayer
@testable import Metatext
class RootViewModelTests: XCTestCase {

View file

@ -2,7 +2,7 @@
import Foundation
import Combine
import Services
import ServiceLayer
class AddIdentityViewModel: ObservableObject {
@Published var urlFieldText = ""

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class EditFilterViewModel: ObservableObject {
@Published var filter: Filter

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class FiltersViewModel: ObservableObject {
@Published var activeFilters = [Filter]()

View file

@ -2,7 +2,7 @@
import Combine
import Foundation
import Services
import ServiceLayer
class IdentitiesViewModel: ObservableObject {
@Published private(set) var identity: Identity

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class ListsViewModel: ObservableObject {
@Published private(set) var lists = [MastodonList]()

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class NotificationTypesPreferencesViewModel: ObservableObject {
@Published var pushSubscriptionAlerts: PushSubscription.Alerts

View file

@ -2,7 +2,7 @@
import Foundation
import Combine
import Services
import ServiceLayer
class PostingReadingPreferencesViewModel: ObservableObject {
@Published var preferences: Identity.Preferences

View file

@ -1,7 +1,7 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import Services
import ServiceLayer
class PreferencesViewModel: ObservableObject {
let handle: String

View file

@ -2,7 +2,7 @@
import Foundation
import Combine
import Services
import ServiceLayer
class RootViewModel: ObservableObject {
@Published private(set) var tabNavigationViewModel: TabNavigationViewModel?

View file

@ -1,7 +1,7 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
import Services
import ServiceLayer
class SecondaryNavigationViewModel: ObservableObject {
@Published private(set) var identity: Identity

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class StatusListViewModel: ObservableObject {
@Published private(set) var statusIDs = [[String]]()

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
struct StatusViewModel {
let content: NSAttributedString

View file

@ -3,7 +3,7 @@
import Foundation
import Combine
import Mastodon
import Services
import ServiceLayer
class TabNavigationViewModel: ObservableObject {
@Published private(set) var identity: Identity

View file

@ -2,7 +2,7 @@
import SwiftUI
import KingfisherSwiftUI
import struct Services.Identity
import struct ServiceLayer.Identity
struct IdentitiesView: View {
@StateObject var viewModel: IdentitiesViewModel