forked from zelo72/mastodon-ios
feat: add Core Data Stack
This commit is contained in:
parent
b1f9047104
commit
1a000b96a8
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20C69" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="HomeTimelineIndex" representedClassName=".HomeTimelineIndex" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="userIdentifier" attributeType="String"/>
|
||||
<relationship name="toots" maxCount="1" deletionRule="Nullify" destinationEntity="Toots" inverseName="homeTimelineIndex" inverseEntity="Toots"/>
|
||||
</entity>
|
||||
<entity name="MastodonUser" representedClassName=".MastodonUser" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="toots" toMany="YES" deletionRule="Nullify" destinationEntity="Toots" inverseName="author" inverseEntity="Toots"/>
|
||||
</entity>
|
||||
<entity name="Toots" representedClassName=".Toots" syncable="YES">
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="toots" inverseEntity="MastodonUser"/>
|
||||
<relationship name="homeTimelineIndex" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="HomeTimelineIndex" inverseName="toots" inverseEntity="HomeTimelineIndex"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Toots" positionX="-248.4609375" positionY="17.3203125" width="128" height="163"/>
|
||||
<element name="MastodonUser" positionX="9.34375" positionY="71.8828125" width="128" height="178"/>
|
||||
<element name="HomeTimelineIndex" positionX="-108" positionY="135" width="128" height="118"/>
|
||||
</elements>
|
||||
</model>
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// CoreDataStack.h
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for CoreDataStack.
|
||||
FOUNDATION_EXPORT double CoreDataStackVersionNumber;
|
||||
|
||||
//! Project version string for CoreDataStack.
|
||||
FOUNDATION_EXPORT const unsigned char CoreDataStackVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <CoreDataStack/PublicHeader.h>
|
||||
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// CoreDataStack.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import os
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
public final class CoreDataStack {
|
||||
|
||||
private(set) var storeDescriptions: [NSPersistentStoreDescription]
|
||||
|
||||
init(persistentStoreDescriptions storeDescriptions: [NSPersistentStoreDescription]) {
|
||||
self.storeDescriptions = storeDescriptions
|
||||
}
|
||||
|
||||
public convenience init(databaseName: String = "shared") {
|
||||
let storeURL = URL.storeURL(for: "group.com.joinmastodon.mastodon-temp", databaseName: databaseName)
|
||||
let storeDescription = NSPersistentStoreDescription(url: storeURL)
|
||||
self.init(persistentStoreDescriptions: [storeDescription])
|
||||
}
|
||||
|
||||
public private(set) lazy var persistentContainer: NSPersistentContainer = {
|
||||
/*
|
||||
The persistent container for the application. This implementation
|
||||
creates and returns a container, having loaded the store for the
|
||||
application to it. This property is optional since there are legitimate
|
||||
error conditions that could cause the creation of the store to fail.
|
||||
*/
|
||||
let container = CoreDataStack.persistentContainer()
|
||||
CoreDataStack.configure(persistentContainer: container, storeDescriptions: storeDescriptions)
|
||||
CoreDataStack.load(persistentContainer: container)
|
||||
|
||||
return container
|
||||
}()
|
||||
|
||||
static func persistentContainer() -> NSPersistentContainer {
|
||||
let bundles = [Bundle(for: Toots.self)]
|
||||
guard let managedObjectModel = NSManagedObjectModel.mergedModel(from: bundles) else {
|
||||
fatalError("cannot locate bundles")
|
||||
}
|
||||
|
||||
let container = NSPersistentContainer(name: "CoreDataStack", managedObjectModel: managedObjectModel)
|
||||
return container
|
||||
}
|
||||
|
||||
static func configure(persistentContainer container: NSPersistentContainer, storeDescriptions: [NSPersistentStoreDescription]) {
|
||||
container.persistentStoreDescriptions = storeDescriptions
|
||||
}
|
||||
|
||||
static func load(persistentContainer container: NSPersistentContainer) {
|
||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||
if let error = error as NSError? {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
||||
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
|
||||
* The device is out of space.
|
||||
* The store could not be migrated to the current model version.
|
||||
Check the error message to determine what the actual problem was.
|
||||
*/
|
||||
if let reason = error.userInfo["reason"] as? String,
|
||||
(reason == "Can't find mapping model for migration" || reason == "Persistent store migration failed, missing mapping model.") {
|
||||
if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
|
||||
try? container.persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: NSSQLiteStoreType, options: nil)
|
||||
os_log("%{public}s[%{public}ld], %{public}s: cannot migrate model. rebuild database…", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
|
||||
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
|
||||
// it's looks like the remote notification only trigger when app enter and leave background
|
||||
container.viewContext.automaticallyMergesChangesFromParent = true
|
||||
|
||||
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, storeDescription.debugDescription)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CoreDataStack {
|
||||
|
||||
public func rebuild() {
|
||||
let oldStoreURL = persistentContainer.persistentStoreCoordinator.url(for: persistentContainer.persistentStoreCoordinator.persistentStores.first!)
|
||||
try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: oldStoreURL, ofType: NSSQLiteStoreType, options: nil)
|
||||
|
||||
CoreDataStack.load(persistentContainer: persistentContainer)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// HomeTimelineIndex.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
final class HomeTimelineIndex: NSManagedObject {
|
||||
|
||||
public typealias ID = String
|
||||
@NSManaged public private(set) var identifier: ID
|
||||
@NSManaged public private(set) var domain: String
|
||||
@NSManaged public private(set) var userIdentifier: String
|
||||
|
||||
@NSManaged public private(set) var createdAt: Date
|
||||
|
||||
// many-to-one relationship
|
||||
@NSManaged public private(set) var toots: Toots
|
||||
|
||||
}
|
||||
|
||||
extension HomeTimelineIndex {
|
||||
|
||||
@discardableResult
|
||||
public static func insert(
|
||||
into context: NSManagedObjectContext,
|
||||
property: Property,
|
||||
toots: Toots
|
||||
) -> HomeTimelineIndex {
|
||||
let index: HomeTimelineIndex = context.insertObject()
|
||||
|
||||
index.identifier = property.identifier
|
||||
index.domain = property.domain
|
||||
index.userIdentifier = toots.author.identifier
|
||||
index.createdAt = toots.createdAt
|
||||
|
||||
index.toots = toots
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension HomeTimelineIndex {
|
||||
public struct Property {
|
||||
public let identifier: String
|
||||
public let domain: String
|
||||
|
||||
public init(domain: String) {
|
||||
self.identifier = UUID().uuidString + "@" + domain
|
||||
self.domain = domain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension HomeTimelineIndex: Managed {
|
||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||
return [NSSortDescriptor(keyPath: \HomeTimelineIndex.createdAt, ascending: false)]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// MastodonUser.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
final class MastodonUser: NSManagedObject {
|
||||
|
||||
public typealias ID = String
|
||||
@NSManaged public private(set) var identifier: ID
|
||||
@NSManaged public private(set) var domain: String
|
||||
|
||||
@NSManaged public private(set) var id: String
|
||||
@NSManaged public private(set) var acct: String
|
||||
@NSManaged public private(set) var username: String
|
||||
@NSManaged public private(set) var displayName: String?
|
||||
|
||||
@NSManaged public private(set) var createdAt: Date
|
||||
@NSManaged public private(set) var updatedAt: Date
|
||||
|
||||
@NSManaged public private(set) var toots: Set<Toots>?
|
||||
|
||||
}
|
||||
|
||||
extension MastodonUser {
|
||||
|
||||
@discardableResult
|
||||
public static func insert(
|
||||
into context: NSManagedObjectContext,
|
||||
property: Property
|
||||
) -> MastodonUser {
|
||||
let user: MastodonUser = context.insertObject()
|
||||
|
||||
user.identifier = property.identifier
|
||||
user.domain = property.domain
|
||||
|
||||
user.id = property.id
|
||||
user.acct = property.acct
|
||||
user.username = property.username
|
||||
user.displayName = property.displayName
|
||||
|
||||
user.createdAt = property.createdAt
|
||||
user.updatedAt = property.networkDate
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MastodonUser {
|
||||
public struct Property {
|
||||
public let identifier: String
|
||||
public let domain: String
|
||||
|
||||
public let id: String
|
||||
public let acct: String
|
||||
public let username: String
|
||||
public let displayName: String?
|
||||
|
||||
public let createdAt: Date
|
||||
public let networkDate: Date
|
||||
|
||||
public init(
|
||||
id: String,
|
||||
domain: String,
|
||||
acct: String,
|
||||
username: String,
|
||||
displayName: String?,
|
||||
content: String,
|
||||
createdAt: Date,
|
||||
networkDate: Date
|
||||
) {
|
||||
self.identifier = id + "@" + domain
|
||||
self.domain = domain
|
||||
self.id = id
|
||||
self.acct = acct
|
||||
self.username = username
|
||||
self.displayName = displayName.flatMap { displayName in
|
||||
return displayName.isEmpty ? nil : displayName
|
||||
}
|
||||
self.createdAt = createdAt
|
||||
self.networkDate = networkDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MastodonUser: Managed {
|
||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||
return [NSSortDescriptor(keyPath: \MastodonUser.createdAt, ascending: false)]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// Toots.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
final class Toots: NSManagedObject {
|
||||
|
||||
public typealias ID = String
|
||||
@NSManaged public private(set) var identifier: ID
|
||||
@NSManaged public private(set) var domain: String
|
||||
|
||||
@NSManaged public private(set) var id: String
|
||||
@NSManaged public private(set) var content: String
|
||||
|
||||
@NSManaged public private(set) var createdAt: Date
|
||||
@NSManaged public private(set) var updatedAt: Date
|
||||
|
||||
// many-to-one relationship
|
||||
@NSManaged public private(set) var author: MastodonUser
|
||||
|
||||
// one-to-many relationship
|
||||
@NSManaged public private(set) var homeTimelineIndexes: Set<HomeTimelineIndex>?
|
||||
|
||||
}
|
||||
|
||||
extension Toots {
|
||||
|
||||
@discardableResult
|
||||
public static func insert(
|
||||
into context: NSManagedObjectContext,
|
||||
property: Property,
|
||||
author: MastodonUser
|
||||
) -> Toots {
|
||||
let toots: Toots = context.insertObject()
|
||||
|
||||
toots.identifier = property.identifier
|
||||
toots.domain = property.domain
|
||||
|
||||
toots.id = property.id
|
||||
toots.content = property.content
|
||||
toots.createdAt = property.createdAt
|
||||
toots.updatedAt = property.networkDate
|
||||
|
||||
toots.author = author
|
||||
|
||||
return toots
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Toots {
|
||||
public struct Property {
|
||||
public let identifier: String
|
||||
public let domain: String
|
||||
|
||||
public let id: String
|
||||
public let content: String
|
||||
public let createdAt: Date
|
||||
public let networkDate: Date
|
||||
|
||||
public init(
|
||||
id: String,
|
||||
domain: String,
|
||||
content: String,
|
||||
createdAt: Date,
|
||||
networkDate: Date
|
||||
) {
|
||||
self.identifier = id + "@" + domain
|
||||
self.domain = domain
|
||||
self.id = id
|
||||
self.content = content
|
||||
self.createdAt = createdAt
|
||||
self.networkDate = networkDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Toots: Managed {
|
||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||
return [NSSortDescriptor(keyPath: \Toots.createdAt, ascending: false)]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Collection.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2020-10-14.
|
||||
// Copyright © 2020 Twidere. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
extension Collection where Iterator.Element: NSManagedObject {
|
||||
public func fetchFaults() {
|
||||
guard !self.isEmpty else { return }
|
||||
guard let context = self.first?.managedObjectContext else {
|
||||
fatalError("Managed object must have context")
|
||||
}
|
||||
let faults = self.filter { $0.isFault }
|
||||
guard let object = faults.first else { return }
|
||||
let request = NSFetchRequest<Iterator.Element>()
|
||||
request.entity = object.entity
|
||||
request.returnsObjectsAsFaults = false
|
||||
request.predicate = NSPredicate(format: "self in %@", faults)
|
||||
do {
|
||||
let _ = try context.fetch(request)
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// NSManagedObjectContext.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2020-8-10.
|
||||
// Copyright © 2020 Dimension. All rights reserved.
|
||||
//
|
||||
|
||||
import os
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreData
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
public func insert<T: NSManagedObject>() -> T where T: Managed {
|
||||
guard let object = NSEntityDescription.insertNewObject(forEntityName: T.entityName, into: self) as? T else {
|
||||
fatalError("cannot insert object: \(T.self)")
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
public func saveOrRollback() throws {
|
||||
do {
|
||||
guard hasChanges else {
|
||||
return
|
||||
}
|
||||
try save()
|
||||
} catch {
|
||||
rollback()
|
||||
|
||||
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public func performChanges(block: @escaping () -> Void) -> Future<Result<Void, Error>, Never> {
|
||||
Future { promise in
|
||||
self.perform {
|
||||
block()
|
||||
do {
|
||||
try self.saveOrRollback()
|
||||
promise(.success(Result.success(())))
|
||||
} catch {
|
||||
promise(.success(Result.failure(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// URL.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension URL {
|
||||
|
||||
/// Returns a URL for the given app group and database pointing to the sqlite database.
|
||||
static func storeURL(for appGroup: String, databaseName: String) -> URL {
|
||||
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
|
||||
fatalError("Shared file container could not be created.")
|
||||
}
|
||||
|
||||
return fileContainer
|
||||
.appendingPathComponent("Databases", isDirectory: true)
|
||||
.appendingPathComponent("\(databaseName).sqlite")
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Managed.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2020-8-6.
|
||||
// Copyright © 2020 Dimension. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
public protocol Managed: class, NSFetchRequestResult {
|
||||
static var entityName: String { get }
|
||||
static var defaultSortDescriptors: [NSSortDescriptor] { get }
|
||||
}
|
||||
|
||||
extension Managed {
|
||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||
return []
|
||||
}
|
||||
|
||||
public static var sortedFetchRequest: NSFetchRequest<Self> {
|
||||
let request = NSFetchRequest<Self>(entityName: entityName)
|
||||
request.sortDescriptors = defaultSortDescriptors
|
||||
return request
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
public func insertObject<T: NSManagedObject>() -> T where T: Managed {
|
||||
guard let object = NSEntityDescription.insertNewObject(forEntityName: T.entityName, into: self) as? T else {
|
||||
fatalError("Wrong object type")
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
extension Managed where Self: NSManagedObject {
|
||||
public static var entityName: String { return entity().name! }
|
||||
}
|
||||
|
||||
extension Managed where Self: NSManagedObject {
|
||||
public static func findOrCreate(in context: NSManagedObjectContext, matching predicate: NSPredicate, configure: (Self) -> Void) -> Self {
|
||||
guard let object = findOrFetch(in: context, matching: predicate) else {
|
||||
let newObject: Self = context.insertObject()
|
||||
configure(newObject)
|
||||
return newObject
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
public static func findOrFetch(in context: NSManagedObjectContext, matching predicate: NSPredicate) -> Self? {
|
||||
guard let object = materializedObject(in: context, matching: predicate) else {
|
||||
return fetch(in: context) { request in
|
||||
request.predicate = predicate
|
||||
request.returnsObjectsAsFaults = false
|
||||
request.fetchLimit = 1
|
||||
}.first
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
public static func materializedObject(in context: NSManagedObjectContext, matching predicate: NSPredicate) -> Self? {
|
||||
for object in context.registeredObjects where !object.isFault {
|
||||
guard let result = object as? Self, predicate.evaluate(with: result) else { continue }
|
||||
return result
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func fetch(in context: NSManagedObjectContext, configurationBlock: (NSFetchRequest<Self>) -> Void = { _ in }) -> [Self] {
|
||||
let request = NSFetchRequest<Self>(entityName: Self.entityName)
|
||||
configurationBlock(request)
|
||||
return try! context.fetch(request)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// NetworkUpdatable.swift
|
||||
// CoreDataStack
|
||||
//
|
||||
// Created by Cirno MainasuK on 2020-9-4.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol NetworkUpdatable {
|
||||
var networkDate: Date { get }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// CoreDataStackTests.swift
|
||||
// CoreDataStackTests
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import CoreDataStack
|
||||
|
||||
class CoreDataStackTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -12,7 +12,6 @@
|
|||
5E44BF88AD33646E64727BCF /* Pods_MastodonTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */; };
|
||||
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 602D783BEC22881EBAD84419 /* Pods_Mastodon.framework */; };
|
||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB3D0FF225BAA61700EAA174 /* AlamofireImage */; };
|
||||
DB3D100025BAA6DA00EAA174 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D0FFF25BAA6DA00EAA174 /* ViewController.swift */; };
|
||||
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB3D100F25BAA75E00EAA174 /* Localizable.strings */; };
|
||||
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D102225BAA7B400EAA174 /* Assets.swift */; };
|
||||
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB3D102325BAA7B400EAA174 /* Strings.swift */; };
|
||||
|
@ -23,6 +22,29 @@
|
|||
DB427DE225BAA00100D1B89D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DB427DE025BAA00100D1B89D /* LaunchScreen.storyboard */; };
|
||||
DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; };
|
||||
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
|
||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||
DB89B9FE25C10FD0008580ED /* CoreDataStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */; };
|
||||
DB89BA0025C10FD0008580ED /* CoreDataStack.h in Headers */ = {isa = PBXBuildFile; fileRef = DB89B9F025C10FD0008580ED /* CoreDataStack.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||
DB89BA0425C10FD0008580ED /* CoreDataStack.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
DB89BA1225C1105C008580ED /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1125C1105C008580ED /* CoreDataStack.swift */; };
|
||||
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1825C1107F008580ED /* Collection.swift */; };
|
||||
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */; };
|
||||
DB89BA1D25C1107F008580ED /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA1A25C1107F008580ED /* URL.swift */; };
|
||||
DB89BA2725C110B4008580ED /* Toots.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA2625C110B4008580ED /* Toots.swift */; };
|
||||
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA3525C1145C008580ED /* CoreData.xcdatamodeld */; };
|
||||
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */; };
|
||||
DB89BA4425C1165F008580ED /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89BA4225C1165F008580ED /* Managed.swift */; };
|
||||
DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52425C131D1002E6C99 /* MastodonUser.swift */; };
|
||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */; };
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52C25C13561002E6C99 /* DocumentStore.swift */; };
|
||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF52D25C13561002E6C99 /* AppContext.swift */; };
|
||||
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */; };
|
||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54325C13647002E6C99 /* NeedsDependency.swift */; };
|
||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */; };
|
||||
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55625C137A8002E6C99 /* HomeViewController.swift */; };
|
||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF55C25C138B7002E6C99 /* UIViewController.swift */; };
|
||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -40,8 +62,43 @@
|
|||
remoteGlobalIDString = DB427DD125BAA00100D1B89D;
|
||||
remoteInfo = Mastodon;
|
||||
};
|
||||
DB89B9F825C10FD0008580ED /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB89B9ED25C10FD0008580ED;
|
||||
remoteInfo = CoreDataStack;
|
||||
};
|
||||
DB89B9FA25C10FD0008580ED /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB427DD125BAA00100D1B89D;
|
||||
remoteInfo = Mastodon;
|
||||
};
|
||||
DB89BA0125C10FD0008580ED /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = DB89B9ED25C10FD0008580ED;
|
||||
remoteInfo = CoreDataStack;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
DB89BA0825C10FD0008580ED /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
DB89BA0425C10FD0008580ED /* CoreDataStack.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
459EA4F43058CAB47719E963 /* Pods-Mastodon-MastodonUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
|
@ -52,7 +109,6 @@
|
|||
BB482D32A7B9825BF5327C4F /* Pods-Mastodon-MastodonUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB3D0FED25BAA42200EAA174 /* MastodonSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonSDK; sourceTree = "<group>"; };
|
||||
DB3D0FFF25BAA6DA00EAA174 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
DB3D100E25BAA75E00EAA174 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DB3D102225BAA7B400EAA174 /* Assets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
||||
DB3D102325BAA7B400EAA174 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
|
@ -69,6 +125,31 @@
|
|||
DB427DF325BAA00100D1B89D /* MastodonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MastodonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = "<group>"; };
|
||||
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB89B9F025C10FD0008580ED /* CoreDataStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreDataStack.h; sourceTree = "<group>"; };
|
||||
DB89B9F125C10FD0008580ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreDataStackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStackTests.swift; sourceTree = "<group>"; };
|
||||
DB89B9FF25C10FD0008580ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Mastodon.entitlements; sourceTree = "<group>"; };
|
||||
DB89BA1125C1105C008580ED /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = "<group>"; };
|
||||
DB89BA1825C1107F008580ED /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = "<group>"; };
|
||||
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContext.swift; sourceTree = "<group>"; };
|
||||
DB89BA1A25C1107F008580ED /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
|
||||
DB89BA2625C110B4008580ED /* Toots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toots.swift; sourceTree = "<group>"; };
|
||||
DB89BA3625C1145C008580ED /* CoreData.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreData.xcdatamodel; sourceTree = "<group>"; };
|
||||
DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUpdatable.swift; sourceTree = "<group>"; };
|
||||
DB89BA4225C1165F008580ED /* Managed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Managed.swift; sourceTree = "<group>"; };
|
||||
DB8AF52425C131D1002E6C99 /* MastodonUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUser.swift; sourceTree = "<group>"; };
|
||||
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewStateStore.swift; sourceTree = "<group>"; };
|
||||
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentStore.swift; sourceTree = "<group>"; };
|
||||
DB8AF52D25C13561002E6C99 /* AppContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = "<group>"; };
|
||||
DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneCoordinator.swift; sourceTree = "<group>"; };
|
||||
DB8AF54325C13647002E6C99 /* NeedsDependency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeedsDependency.swift; sourceTree = "<group>"; };
|
||||
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
|
||||
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
|
||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineIndex.swift; sourceTree = "<group>"; };
|
||||
EC6E707B68A67DB08EC288FA /* Pods-MastodonTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.debug.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -77,6 +158,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB89BA0325C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
||||
5D526FE225BE9AC400460CB9 /* MastodonSDK in Frameworks */,
|
||||
7A9135D4559750AF07CA9BE4 /* Pods_Mastodon.framework in Frameworks */,
|
||||
DB3D0FF325BAA61700EAA174 /* AlamofireImage in Frameworks */,
|
||||
|
@ -99,6 +181,21 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EB25C10FD0008580ED /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9F325C10FD0008580ED /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
|
@ -161,6 +258,8 @@
|
|||
DB427DD425BAA00100D1B89D /* Mastodon */,
|
||||
DB427DEB25BAA00100D1B89D /* MastodonTests */,
|
||||
DB427DF625BAA00100D1B89D /* MastodonUITests */,
|
||||
DB89B9EF25C10FD0008580ED /* CoreDataStack */,
|
||||
DB89B9FC25C10FD0008580ED /* CoreDataStackTests */,
|
||||
DB427DD325BAA00100D1B89D /* Products */,
|
||||
1EBA4F56E920856A3FC84ACB /* Pods */,
|
||||
4E8E8B18DB8471A676012CF9 /* Frameworks */,
|
||||
|
@ -173,6 +272,8 @@
|
|||
DB427DD225BAA00100D1B89D /* Mastodon.app */,
|
||||
DB427DE825BAA00100D1B89D /* MastodonTests.xctest */,
|
||||
DB427DF325BAA00100D1B89D /* MastodonUITests.xctest */,
|
||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */,
|
||||
DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -180,8 +281,12 @@
|
|||
DB427DD425BAA00100D1B89D /* Mastodon */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89BA1025C10FF5008580ED /* Mastodon.entitlements */,
|
||||
DB427DE325BAA00100D1B89D /* Info.plist */,
|
||||
DB3D0FFF25BAA6DA00EAA174 /* ViewController.swift */,
|
||||
DB8AF52A25C13561002E6C99 /* State */,
|
||||
DB8AF56225C138BC002E6C99 /* Extension */,
|
||||
DB8AF55525C1379F002E6C99 /* Scene */,
|
||||
DB8AF54125C13647002E6C99 /* Coordinator */,
|
||||
DB3D101B25BAA79200EAA174 /* Generated */,
|
||||
DB3D0FF825BAA6B200EAA174 /* Resources */,
|
||||
DB3D0FF725BAA68500EAA174 /* Supporting Files */,
|
||||
|
@ -207,8 +312,115 @@
|
|||
path = MastodonUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89B9EF25C10FD0008580ED /* CoreDataStack */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89B9F125C10FD0008580ED /* Info.plist */,
|
||||
DB89B9F025C10FD0008580ED /* CoreDataStack.h */,
|
||||
DB89BA1125C1105C008580ED /* CoreDataStack.swift */,
|
||||
DB89BA3525C1145C008580ED /* CoreData.xcdatamodeld */,
|
||||
DB89BA4025C1165F008580ED /* Protocol */,
|
||||
DB89BA1725C1107F008580ED /* Extension */,
|
||||
DB89BA2C25C110B7008580ED /* Entity */,
|
||||
);
|
||||
path = CoreDataStack;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89B9FC25C10FD0008580ED /* CoreDataStackTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */,
|
||||
DB89B9FF25C10FD0008580ED /* Info.plist */,
|
||||
);
|
||||
path = CoreDataStackTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89BA1725C1107F008580ED /* Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89BA1825C1107F008580ED /* Collection.swift */,
|
||||
DB89BA1925C1107F008580ED /* NSManagedObjectContext.swift */,
|
||||
DB89BA1A25C1107F008580ED /* URL.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89BA2C25C110B7008580ED /* Entity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89BA2625C110B4008580ED /* Toots.swift */,
|
||||
DB8AF52425C131D1002E6C99 /* MastodonUser.swift */,
|
||||
DB8AF56725C13E2A002E6C99 /* HomeTimelineIndex.swift */,
|
||||
);
|
||||
path = Entity;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89BA4025C1165F008580ED /* Protocol */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB89BA4125C1165F008580ED /* NetworkUpdatable.swift */,
|
||||
DB89BA4225C1165F008580ED /* Managed.swift */,
|
||||
);
|
||||
path = Protocol;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB8AF52A25C13561002E6C99 /* State */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF52B25C13561002E6C99 /* ViewStateStore.swift */,
|
||||
DB8AF52C25C13561002E6C99 /* DocumentStore.swift */,
|
||||
DB8AF52D25C13561002E6C99 /* AppContext.swift */,
|
||||
);
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB8AF54125C13647002E6C99 /* Coordinator */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF54225C13647002E6C99 /* SceneCoordinator.swift */,
|
||||
DB8AF54325C13647002E6C99 /* NeedsDependency.swift */,
|
||||
);
|
||||
path = Coordinator;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB8AF54E25C13703002E6C99 /* MainTab */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */,
|
||||
);
|
||||
path = MainTab;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB8AF55525C1379F002E6C99 /* Scene */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF54E25C13703002E6C99 /* MainTab */,
|
||||
DB8AF55625C137A8002E6C99 /* HomeViewController.swift */,
|
||||
);
|
||||
path = Scene;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB8AF56225C138BC002E6C99 /* Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF55C25C138B7002E6C99 /* UIViewController.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
DB89B9E925C10FD0008580ED /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB89BA0025C10FD0008580ED /* CoreDataStack.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
DB427DD125BAA00100D1B89D /* Mastodon */ = {
|
||||
isa = PBXNativeTarget;
|
||||
|
@ -220,10 +432,12 @@
|
|||
DB427DD025BAA00100D1B89D /* Resources */,
|
||||
5532CB85BBE168B25B20720B /* [CP] Embed Pods Frameworks */,
|
||||
DB3D100425BAA71500EAA174 /* ShellScript */,
|
||||
DB89BA0825C10FD0008580ED /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DB89BA0225C10FD0008580ED /* PBXTargetDependency */,
|
||||
);
|
||||
name = Mastodon;
|
||||
packageProductDependencies = (
|
||||
|
@ -273,6 +487,43 @@
|
|||
productReference = DB427DF325BAA00100D1B89D /* MastodonUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
DB89B9ED25C10FD0008580ED /* CoreDataStack */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DB89BA0525C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStack" */;
|
||||
buildPhases = (
|
||||
DB89B9E925C10FD0008580ED /* Headers */,
|
||||
DB89B9EA25C10FD0008580ED /* Sources */,
|
||||
DB89B9EB25C10FD0008580ED /* Frameworks */,
|
||||
DB89B9EC25C10FD0008580ED /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = CoreDataStack;
|
||||
productName = CoreDataStack;
|
||||
productReference = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
DB89B9F525C10FD0008580ED /* CoreDataStackTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DB89BA0925C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStackTests" */;
|
||||
buildPhases = (
|
||||
DB89B9F225C10FD0008580ED /* Sources */,
|
||||
DB89B9F325C10FD0008580ED /* Frameworks */,
|
||||
DB89B9F425C10FD0008580ED /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DB89B9F925C10FD0008580ED /* PBXTargetDependency */,
|
||||
DB89B9FB25C10FD0008580ED /* PBXTargetDependency */,
|
||||
);
|
||||
name = CoreDataStackTests;
|
||||
productName = CoreDataStackTests;
|
||||
productReference = DB89B9F625C10FD0008580ED /* CoreDataStackTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
|
@ -293,6 +544,14 @@
|
|||
CreatedOnToolsVersion = 12.4;
|
||||
TestTargetID = DB427DD125BAA00100D1B89D;
|
||||
};
|
||||
DB89B9ED25C10FD0008580ED = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
LastSwiftMigration = 1240;
|
||||
};
|
||||
DB89B9F525C10FD0008580ED = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
TestTargetID = DB427DD125BAA00100D1B89D;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = DB427DCD25BAA00100D1B89D /* Build configuration list for PBXProject "Mastodon" */;
|
||||
|
@ -314,6 +573,8 @@
|
|||
DB427DD125BAA00100D1B89D /* Mastodon */,
|
||||
DB427DE725BAA00100D1B89D /* MastodonTests */,
|
||||
DB427DF225BAA00100D1B89D /* MastodonUITests */,
|
||||
DB89B9ED25C10FD0008580ED /* CoreDataStack */,
|
||||
DB89B9F525C10FD0008580ED /* CoreDataStackTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -344,6 +605,20 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EC25C10FD0008580ED /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9F425C10FD0008580ED /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
|
@ -471,8 +746,15 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB3D100025BAA6DA00EAA174 /* ViewController.swift in Sources */,
|
||||
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
|
||||
DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */,
|
||||
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
|
||||
DB8AF55025C13703002E6C99 /* MainTabBarController.swift in Sources */,
|
||||
DB8AF54525C13647002E6C99 /* NeedsDependency.swift in Sources */,
|
||||
DB427DD625BAA00100D1B89D /* AppDelegate.swift in Sources */,
|
||||
DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */,
|
||||
DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */,
|
||||
DB8AF55725C137A8002E6C99 /* HomeViewController.swift in Sources */,
|
||||
DB3D102525BAA7B400EAA174 /* Strings.swift in Sources */,
|
||||
DB3D102425BAA7B400EAA174 /* Assets.swift in Sources */,
|
||||
DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */,
|
||||
|
@ -495,6 +777,31 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9EA25C10FD0008580ED /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB89BA1225C1105C008580ED /* CoreDataStack.swift in Sources */,
|
||||
DB89BA1C25C1107F008580ED /* NSManagedObjectContext.swift in Sources */,
|
||||
DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */,
|
||||
DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */,
|
||||
DB89BA1B25C1107F008580ED /* Collection.swift in Sources */,
|
||||
DB89BA2725C110B4008580ED /* Toots.swift in Sources */,
|
||||
DB89BA4425C1165F008580ED /* Managed.swift in Sources */,
|
||||
DB89BA4325C1165F008580ED /* NetworkUpdatable.swift in Sources */,
|
||||
DB8AF56825C13E2A002E6C99 /* HomeTimelineIndex.swift in Sources */,
|
||||
DB89BA1D25C1107F008580ED /* URL.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
DB89B9F225C10FD0008580ED /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DB89B9FE25C10FD0008580ED /* CoreDataStackTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
|
@ -508,6 +815,21 @@
|
|||
target = DB427DD125BAA00100D1B89D /* Mastodon */;
|
||||
targetProxy = DB427DF425BAA00100D1B89D /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB89B9F925C10FD0008580ED /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB89B9ED25C10FD0008580ED /* CoreDataStack */;
|
||||
targetProxy = DB89B9F825C10FD0008580ED /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB89B9FB25C10FD0008580ED /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB427DD125BAA00100D1B89D /* Mastodon */;
|
||||
targetProxy = DB89B9FA25C10FD0008580ED /* PBXContainerItemProxy */;
|
||||
};
|
||||
DB89BA0225C10FD0008580ED /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = DB89B9ED25C10FD0008580ED /* CoreDataStack */;
|
||||
targetProxy = DB89BA0125C10FD0008580ED /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
@ -589,7 +911,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
@ -644,7 +966,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -658,8 +980,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -679,8 +1003,10 @@
|
|||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 75E3471C898DDD9631729B6E /* Pods-Mastodon.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -780,6 +1106,103 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
DB89BA0625C10FD0008580ED /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.CoreDataStack;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
DB89BA0725C10FD0008580ED /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.CoreDataStack;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
DB89BA0A25C10FD0008580ED /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = CoreDataStackTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.CoreDataStackTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mastodon.app/Mastodon";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
DB89BA0B25C10FD0008580ED /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7LFDZ96332;
|
||||
INFOPLIST_FILE = CoreDataStackTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.CoreDataStackTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mastodon.app/Mastodon";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
|
@ -819,6 +1242,24 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
DB89BA0525C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStack" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DB89BA0625C10FD0008580ED /* Debug */,
|
||||
DB89BA0725C10FD0008580ED /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
DB89BA0925C10FD0008580ED /* Build configuration list for PBXNativeTarget "CoreDataStackTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DB89BA0A25C10FD0008580ED /* Debug */,
|
||||
DB89BA0B25C10FD0008580ED /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
|
@ -843,6 +1284,19 @@
|
|||
productName = AlamofireImage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DB89BA3525C1145C008580ED /* CoreData.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DB89BA3625C1145C008580ED /* CoreData.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DB89BA3625C1145C008580ED /* CoreData.xcdatamodel */;
|
||||
path = CoreData.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = DB427DCA25BAA00100D1B89D /* Project object */;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>Mastodon.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// NeedsDependency.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol NeedsDependency: class {
|
||||
var context: AppContext! { get set }
|
||||
var coordinator: SceneCoordinator! { get set }
|
||||
}
|
||||
|
||||
extension UISceneSession {
|
||||
private struct AssociatedKeys {
|
||||
static var sceneCoordinator = "SceneCoordinator"
|
||||
}
|
||||
|
||||
weak var sceneCoordinator: SceneCoordinator? {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.sceneCoordinator) as? SceneCoordinator
|
||||
}
|
||||
set {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.sceneCoordinator, newValue, .OBJC_ASSOCIATION_ASSIGN)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// SceneCoordinator.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
|
||||
import UIKit
|
||||
import SafariServices
|
||||
|
||||
final public class SceneCoordinator {
|
||||
|
||||
private weak var scene: UIScene!
|
||||
private weak var sceneDelegate: SceneDelegate!
|
||||
private weak var appContext: AppContext!
|
||||
|
||||
let id = UUID().uuidString
|
||||
|
||||
init(scene: UIScene, sceneDelegate: SceneDelegate, appContext: AppContext) {
|
||||
self.scene = scene
|
||||
self.sceneDelegate = sceneDelegate
|
||||
self.appContext = appContext
|
||||
|
||||
scene.session.sceneCoordinator = self
|
||||
}
|
||||
}
|
||||
|
||||
extension SceneCoordinator {
|
||||
enum Transition {
|
||||
case show // push
|
||||
case showDetail // replace
|
||||
case modal(animated: Bool, completion: (() -> Void)? = nil)
|
||||
case custom(transitioningDelegate: UIViewControllerTransitioningDelegate)
|
||||
case customPush
|
||||
case safariPresent(animated: Bool, completion: (() -> Void)? = nil)
|
||||
case activityViewControllerPresent(animated: Bool, completion: (() -> Void)? = nil)
|
||||
case alertController(animated: Bool, completion: (() -> Void)? = nil)
|
||||
}
|
||||
|
||||
enum Scene {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension SceneCoordinator {
|
||||
|
||||
func setup() {
|
||||
let viewController = MainTabBarController(context: appContext, coordinator: self)
|
||||
sceneDelegate.window?.rootViewController = viewController
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func present(scene: Scene, from sender: UIViewController?, transition: Transition) -> UIViewController? {
|
||||
guard let viewController = get(scene: scene) else {
|
||||
return nil
|
||||
}
|
||||
guard var presentingViewController = sender ?? sceneDelegate.window?.rootViewController?.topMost else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let mainTabBarController = presentingViewController as? MainTabBarController,
|
||||
let navigationController = mainTabBarController.selectedViewController as? UINavigationController,
|
||||
let topViewController = navigationController.topViewController {
|
||||
presentingViewController = topViewController
|
||||
}
|
||||
|
||||
switch transition {
|
||||
case .show:
|
||||
presentingViewController.show(viewController, sender: sender)
|
||||
|
||||
case .showDetail:
|
||||
let navigationController = UINavigationController(rootViewController: viewController)
|
||||
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
||||
|
||||
case .modal(let animated, let completion):
|
||||
let modalNavigationController = UINavigationController(rootViewController: viewController)
|
||||
if let adaptivePresentationControllerDelegate = viewController as? UIAdaptivePresentationControllerDelegate {
|
||||
modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate
|
||||
}
|
||||
presentingViewController.present(modalNavigationController, animated: animated, completion: completion)
|
||||
|
||||
case .custom(let transitioningDelegate):
|
||||
viewController.modalPresentationStyle = .custom
|
||||
viewController.transitioningDelegate = transitioningDelegate
|
||||
sender?.present(viewController, animated: true, completion: nil)
|
||||
|
||||
case .customPush:
|
||||
// set delegate in view controller
|
||||
assert(sender?.navigationController?.delegate != nil)
|
||||
sender?.navigationController?.pushViewController(viewController, animated: true)
|
||||
|
||||
case .safariPresent(let animated, let completion):
|
||||
presentingViewController.present(viewController, animated: animated, completion: completion)
|
||||
|
||||
case .activityViewControllerPresent(let animated, let completion):
|
||||
presentingViewController.present(viewController, animated: animated, completion: completion)
|
||||
|
||||
case .alertController(let animated, let completion):
|
||||
presentingViewController.present(viewController, animated: animated, completion: completion)
|
||||
}
|
||||
|
||||
return viewController
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension SceneCoordinator {
|
||||
|
||||
func get(scene: Scene) -> UIViewController? {
|
||||
let viewController: UIViewController?
|
||||
|
||||
// TODO:
|
||||
viewController = nil
|
||||
|
||||
setupDependency(for: viewController as? NeedsDependency)
|
||||
|
||||
return viewController
|
||||
}
|
||||
|
||||
private func setupDependency(for needs: NeedsDependency?) {
|
||||
needs?.context = appContext
|
||||
needs?.coordinator = self
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// UIViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
/// Returns the top most view controller from given view controller's stack.
|
||||
var topMost: UIViewController? {
|
||||
// presented view controller
|
||||
if let presentedViewController = presentedViewController {
|
||||
return presentedViewController.topMost
|
||||
}
|
||||
|
||||
// UITabBarController
|
||||
if let tabBarController = self as? UITabBarController,
|
||||
let selectedViewController = tabBarController.selectedViewController {
|
||||
return selectedViewController.topMost
|
||||
}
|
||||
|
||||
// UINavigationController
|
||||
if let navigationController = self as? UINavigationController,
|
||||
let visibleViewController = navigationController.visibleViewController {
|
||||
return visibleViewController.topMost
|
||||
}
|
||||
|
||||
// UIPageController
|
||||
if let pageViewController = self as? UIPageViewController,
|
||||
pageViewController.viewControllers?.count == 1 {
|
||||
return pageViewController.viewControllers?.first?.topMost ?? self
|
||||
}
|
||||
|
||||
// child view controller
|
||||
for subview in self.view?.subviews ?? [] {
|
||||
if let childViewController = subview.next as? UIViewController {
|
||||
return childViewController.topMost
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension UIViewController {
|
||||
|
||||
/// https://bluelemonbits.com/2018/08/26/inserting-cells-at-the-top-of-a-uitableview-with-no-scrolling/
|
||||
static func topVisibleTableViewCellIndexPath(in tableView: UITableView, navigationBar: UINavigationBar) -> IndexPath? {
|
||||
let navigationBarRectInTableView = tableView.convert(navigationBar.bounds, from: navigationBar)
|
||||
let navigationBarMaxYPosition = CGPoint(x: 0, y: navigationBarRectInTableView.origin.y + navigationBarRectInTableView.size.height + 1) // +1pt for UIKit cell locate
|
||||
let mostTopVisiableIndexPath = tableView.indexPathForRow(at: navigationBarMaxYPosition)
|
||||
return mostTopVisiableIndexPath
|
||||
}
|
||||
|
||||
static func tableViewCellOriginOffsetToWindowTop(in tableView: UITableView, at indexPath: IndexPath, navigationBar: UINavigationBar) -> CGFloat {
|
||||
let rectForTopRow = tableView.rectForRow(at: indexPath)
|
||||
let navigationBarRectInTableView = tableView.convert(navigationBar.bounds, from: navigationBar)
|
||||
let navigationBarMaxYPosition = CGPoint(x: 0, y: navigationBarRectInTableView.origin.y + navigationBarRectInTableView.size.height) // without +1pt
|
||||
let differenceBetweenTopRowAndNavigationBar = rectForTopRow.origin.y - navigationBarMaxYPosition.y
|
||||
return differenceBetweenTopRowAndNavigationBar
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.joinmastodon.mastodon-temp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// HomeViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class HomeViewController: UIViewController, NeedsDependency {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension HomeViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
title = "Home"
|
||||
view.backgroundColor = .systemBackground
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// MainTabBarController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import SafariServices
|
||||
|
||||
class MainTabBarController: UITabBarController {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
weak var context: AppContext!
|
||||
weak var coordinator: SceneCoordinator!
|
||||
|
||||
enum Tab: Int, CaseIterable {
|
||||
case home
|
||||
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .home: return "Home"
|
||||
}
|
||||
}
|
||||
|
||||
var image: UIImage {
|
||||
switch self {
|
||||
case .home: return UIImage(systemName: "house")!
|
||||
}
|
||||
}
|
||||
|
||||
func viewController(context: AppContext, coordinator: SceneCoordinator) -> UIViewController {
|
||||
let viewController: UIViewController
|
||||
switch self {
|
||||
case .home:
|
||||
let _viewController = HomeViewController()
|
||||
_viewController.context = context
|
||||
_viewController.coordinator = coordinator
|
||||
viewController = _viewController
|
||||
}
|
||||
viewController.title = self.title
|
||||
return UINavigationController(rootViewController: viewController)
|
||||
}
|
||||
}
|
||||
|
||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
||||
self.context = context
|
||||
self.coordinator = coordinator
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MainTabBarController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = .systemBackground
|
||||
|
||||
let tabs = Tab.allCases
|
||||
let viewControllers: [UIViewController] = tabs.map { tab in
|
||||
let viewController = tab.viewController(context: context, coordinator: coordinator)
|
||||
viewController.tabBarItem.title = "" // set text to empty string for image only style (SDK failed to layout when set to nil)
|
||||
viewController.tabBarItem.image = tab.image
|
||||
return viewController
|
||||
}
|
||||
setViewControllers(viewControllers, animated: false)
|
||||
selectedIndex = 0
|
||||
|
||||
// TODO: custom accent color
|
||||
let tabBarAppearance = UITabBarAppearance()
|
||||
tabBarAppearance.configureWithDefaultBackground()
|
||||
tabBar.standardAppearance = tabBarAppearance
|
||||
|
||||
#if DEBUG
|
||||
// selectedIndex = 1
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// AppContext.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
|
||||
class AppContext: ObservableObject {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
@Published var viewStateStore = ViewStateStore()
|
||||
|
||||
let coreDataStack: CoreDataStack
|
||||
let managedObjectContext: NSManagedObjectContext
|
||||
let backgroundManagedObjectContext: NSManagedObjectContext
|
||||
|
||||
|
||||
let documentStore: DocumentStore
|
||||
private var documentStoreSubscription: AnyCancellable!
|
||||
|
||||
let overrideTraitCollection = CurrentValueSubject<UITraitCollection?, Never>(nil)
|
||||
|
||||
init() {
|
||||
let _coreDataStack = CoreDataStack()
|
||||
let _managedObjectContext = _coreDataStack.persistentContainer.viewContext
|
||||
let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext()
|
||||
coreDataStack = _coreDataStack
|
||||
managedObjectContext = _managedObjectContext
|
||||
backgroundManagedObjectContext = _backgroundManagedObjectContext
|
||||
|
||||
documentStore = DocumentStore()
|
||||
documentStoreSubscription = documentStore.objectWillChange
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [unowned self] in
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
backgroundManagedObjectContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: backgroundManagedObjectContext)
|
||||
.sink { [weak self] notification in
|
||||
guard let self = self else { return }
|
||||
self.managedObjectContext.perform {
|
||||
self.managedObjectContext.mergeChanges(fromContextDidSave: notification)
|
||||
}
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// DocumentStore.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
class DocumentStore: ObservableObject { }
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// ViewStateStore.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-1-27.
|
||||
//
|
||||
|
||||
import Combine
|
||||
|
||||
struct ViewStateStore {
|
||||
|
||||
}
|
||||
|
||||
enum ViewState { }
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
let appContext = AppContext()
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
|
@ -34,3 +34,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
}
|
||||
|
||||
|
||||
extension AppDelegate {
|
||||
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
|
||||
#if DEBUG
|
||||
return .all
|
||||
#else
|
||||
return UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension AppContext {
|
||||
static var shared: AppContext {
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
return appDelegate.appContext
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="BYZ-38-t0r" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="226" y="166"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -10,13 +10,22 @@ import UIKit
|
|||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
var coordinator: SceneCoordinator?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
guard let _ = (scene as? UIWindowScene) else { return }
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
self.window = window
|
||||
|
||||
|
||||
let appContext = AppContext.shared
|
||||
let sceneCoordinator = SceneCoordinator(scene: scene, sceneDelegate: self, appContext: appContext)
|
||||
self.coordinator = sceneCoordinator
|
||||
|
||||
sceneCoordinator.setup()
|
||||
|
||||
window.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
//
|
||||
// ViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021/1/22.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MastodonSDK
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -9,10 +9,14 @@ import Combine
|
|||
import Foundation
|
||||
|
||||
public extension Mastodon.API.App {
|
||||
|
||||
|
||||
static func appEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("apps")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Mastodon.API.App {
|
||||
|
||||
struct Application: Codable {
|
||||
|
||||
|
|
Loading…
Reference in New Issue