Update iOS with latest TimelineModel refactoring
This commit is contained in:
parent
7d7a018fe1
commit
882ebbea3e
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// ReplaySubject.swift
|
||||
// CombineExt
|
||||
//
|
||||
// Created by Jasdev Singh on 13/04/2020.
|
||||
// Copyright © 2020 Combine Community. All rights reserved.
|
||||
//
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
/// A `ReplaySubject` is a subject that can buffer one or more values. It stores value events, up to its `bufferSize` in a
|
||||
/// first-in-first-out manner and then replays it to
|
||||
/// future subscribers and also forwards completion events.
|
||||
///
|
||||
/// The implementation borrows heavily from [Entwine’s](https://github.com/tcldr/Entwine/blob/b839c9fcc7466878d6a823677ce608da998b95b9/Sources/Entwine/Operators/ReplaySubject.swift).
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public final class ReplaySubject<Output, Failure: Error>: Subject {
|
||||
public typealias Output = Output
|
||||
public typealias Failure = Failure
|
||||
|
||||
private let bufferSize: Int
|
||||
private var buffer = [Output]()
|
||||
|
||||
// Keeping track of all live subscriptions, so `send` events can be forwarded to them.
|
||||
private var subscriptions = [Subscription<AnySubscriber<Output, Failure>>]()
|
||||
|
||||
private var completion: Subscribers.Completion<Failure>?
|
||||
private var isActive: Bool { completion == nil }
|
||||
|
||||
/// Create a `ReplaySubject`, buffering up to `bufferSize` values and replaying them to new subscribers
|
||||
/// - Parameter bufferSize: The maximum number of value events to buffer and replay to all future subscribers.
|
||||
public init(bufferSize: Int) {
|
||||
self.bufferSize = bufferSize
|
||||
}
|
||||
|
||||
public func send(_ value: Output) {
|
||||
guard isActive else { return }
|
||||
|
||||
buffer.append(value)
|
||||
|
||||
if buffer.count > bufferSize {
|
||||
buffer.removeFirst()
|
||||
}
|
||||
|
||||
subscriptions.forEach { $0.forwardValueToBuffer(value) }
|
||||
}
|
||||
|
||||
public func send(completion: Subscribers.Completion<Failure>) {
|
||||
guard isActive else { return }
|
||||
|
||||
self.completion = completion
|
||||
|
||||
subscriptions.forEach { $0.forwardCompletionToBuffer(completion) }
|
||||
}
|
||||
|
||||
public func send(subscription: Combine.Subscription) {
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
|
||||
public func receive<Subscriber: Combine.Subscriber>(subscriber: Subscriber) where Failure == Subscriber.Failure, Output == Subscriber.Input {
|
||||
let subscriberIdentifier = subscriber.combineIdentifier
|
||||
|
||||
let subscription = Subscription(downstream: AnySubscriber(subscriber)) { [weak self] in
|
||||
guard let self = self,
|
||||
let subscriptionIndex = self.subscriptions
|
||||
.firstIndex(where: { $0.innerSubscriberIdentifier == subscriberIdentifier }) else { return }
|
||||
|
||||
self.subscriptions.remove(at: subscriptionIndex)
|
||||
}
|
||||
|
||||
subscriptions.append(subscription)
|
||||
|
||||
subscriber.receive(subscription: subscription)
|
||||
subscription.replay(buffer, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
extension ReplaySubject {
|
||||
final class Subscription<Downstream: Subscriber>: Combine.Subscription where Output == Downstream.Input, Failure == Downstream.Failure {
|
||||
private var demandBuffer: DemandBuffer<Downstream>?
|
||||
private var cancellationHandler: (() -> Void)?
|
||||
|
||||
fileprivate let innerSubscriberIdentifier: CombineIdentifier
|
||||
|
||||
init(downstream: Downstream, cancellationHandler: (() -> Void)?) {
|
||||
self.demandBuffer = DemandBuffer(subscriber: downstream)
|
||||
self.innerSubscriberIdentifier = downstream.combineIdentifier
|
||||
self.cancellationHandler = cancellationHandler
|
||||
}
|
||||
|
||||
func replay(_ buffer: [Output], completion: Subscribers.Completion<Failure>?) {
|
||||
buffer.forEach(forwardValueToBuffer)
|
||||
|
||||
if let completion = completion {
|
||||
forwardCompletionToBuffer(completion)
|
||||
}
|
||||
}
|
||||
|
||||
func forwardValueToBuffer(_ value: Output) {
|
||||
_ = demandBuffer?.buffer(value: value)
|
||||
}
|
||||
|
||||
func forwardCompletionToBuffer(_ completion: Subscribers.Completion<Failure>) {
|
||||
demandBuffer?.complete(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
_ = demandBuffer?.demand(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
cancellationHandler?()
|
||||
cancellationHandler = nil
|
||||
|
||||
demandBuffer = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// ShareReplay.swift
|
||||
// CombineExt
|
||||
//
|
||||
// Created by Jasdev Singh on 13/04/2020.
|
||||
// Copyright © 2020 Combine Community. All rights reserved.
|
||||
//
|
||||
|
||||
#if canImport(Combine)
|
||||
import Combine
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public extension Publisher {
|
||||
/// A variation on [share()](https://developer.apple.com/documentation/combine/publisher/3204754-share)
|
||||
/// that allows for buffering and replaying a `replay` amount of value events to future subscribers.
|
||||
///
|
||||
/// - Parameter count: The number of value events to buffer in a first-in-first-out manner.
|
||||
/// - Returns: A publisher that replays the specified number of value events to future subscribers.
|
||||
func share(replay count: Int) -> Publishers.Autoconnect<Publishers.Multicast<Self, ReplaySubject<Output, Failure>>> {
|
||||
multicast { ReplaySubject(bufferSize: count) }
|
||||
.autoconnect()
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -75,6 +75,7 @@ private extension SidebarModel {
|
|||
.removeDuplicates(by: { previousFeeds, currentFeeds in
|
||||
return previousFeeds.elementsEqual(currentFeeds, by: { $0.feedID == $1.feedID })
|
||||
})
|
||||
.share(replay: 1)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
@ -107,7 +108,7 @@ private extension SidebarModel {
|
|||
.compactMap { [weak self] _, readFilter, selectedFeeds in
|
||||
self?.rebuildSidebarItems(isReadFiltered: readFilter, selectedFeeds: selectedFeeds)
|
||||
}
|
||||
.share()
|
||||
.share(replay: 1)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
weak var delegate: TimelineModelDelegate?
|
||||
|
||||
@Published var nameForDisplay = ""
|
||||
@Published var selectedArticleIDs = Set<String>() // Don't use directly. Use selectedArticles
|
||||
@Published var selectedArticleID: String? = nil // Don't use directly. Use selectedArticles
|
||||
@Published var selectedTimelineItemIDs = Set<String>() // Don't use directly. Use selectedTimelineItemsPublisher
|
||||
@Published var selectedTimelineItemID: String? = nil // Don't use directly. Use selectedTimelineItemsPublisher
|
||||
@Published var isReadFiltered: Bool? = nil
|
||||
|
||||
var timelineItemsPublisher: AnyPublisher<[TimelineItem], Never>?
|
||||
|
@ -40,15 +40,16 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var sortDirectionSubject = PassthroughSubject<Bool, Never>()
|
||||
private var groupByFeedSubject = PassthroughSubject<Bool, Never>()
|
||||
private var sortDirectionSubject = ReplaySubject<Bool, Never>(bufferSize: 1)
|
||||
private var groupByFeedSubject = ReplaySubject<Bool, Never>(bufferSize: 1)
|
||||
|
||||
init(delegate: TimelineModelDelegate) {
|
||||
self.delegate = delegate
|
||||
// subscribeToArticleStatusChanges()
|
||||
subscribeToUserDefaultsChanges()
|
||||
subscribeToReadFilterChanges()
|
||||
subscribeToArticleFetchChanges()
|
||||
subscribeToSelectedArticleSelectionChanges()
|
||||
// subscribeToArticleStatusChanges()
|
||||
// subscribeToAccountDidDownloadArticles()
|
||||
}
|
||||
|
||||
|
@ -78,6 +79,31 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
// }.store(in: &cancellables)
|
||||
// }
|
||||
|
||||
func subscribeToReadFilterChanges() {
|
||||
guard let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return }
|
||||
|
||||
selectedFeedsPublisher.sink { [weak self] feeds in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard feeds.count == 1, let timelineFeed = feeds.first else {
|
||||
self.isReadFiltered = nil
|
||||
return
|
||||
}
|
||||
|
||||
guard timelineFeed.defaultReadFilterType != .alwaysRead else {
|
||||
self.isReadFiltered = nil
|
||||
return
|
||||
}
|
||||
|
||||
if let feedID = timelineFeed.feedID, let readFilterEnabled = self.readFilterEnabledTable[feedID] {
|
||||
self.isReadFiltered = readFilterEnabled
|
||||
} else {
|
||||
self.isReadFiltered = timelineFeed.defaultReadFilterType == .read
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func subscribeToUserDefaultsChanges() {
|
||||
let kickStartNote = Notification(name: Notification.Name("Kick Start"))
|
||||
NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification)
|
||||
|
@ -97,11 +123,12 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
.map { [weak self] feeds -> Set<Article> in
|
||||
return self?.fetchArticles(feeds: feeds) ?? Set<Article>()
|
||||
}
|
||||
.combineLatest($isReadFiltered, sortDirectionPublisher, groupByPublisher)
|
||||
.compactMap { [weak self] articles, filtered, sortDirection, groupBy -> [TimelineItem] in
|
||||
.combineLatest(sortDirectionPublisher, groupByPublisher)
|
||||
.compactMap { [weak self] articles, sortDirection, groupBy -> [TimelineItem] in
|
||||
let sortedArticles = Array(articles).sortedByDate(sortDirection ? .orderedDescending : .orderedAscending, groupByFeed: groupBy)
|
||||
return self?.buildTimelineItems(articles: sortedArticles) ?? [TimelineItem]()
|
||||
}
|
||||
.share(replay: 1)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
@ -218,24 +245,6 @@ private extension TimelineModel {
|
|||
}
|
||||
|
||||
// MARK: Timeline Management
|
||||
|
||||
// func resetReadFilter() {
|
||||
// guard feeds.count == 1, let timelineFeed = feeds.first else {
|
||||
// isReadFiltered = nil
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// guard timelineFeed.defaultReadFilterType != .alwaysRead else {
|
||||
// isReadFiltered = nil
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if let feedID = timelineFeed.feedID, let readFilterEnabled = readFilterEnabledTable[feedID] {
|
||||
// isReadFiltered = readFilterEnabled
|
||||
// } else {
|
||||
// isReadFiltered = timelineFeed.defaultReadFilterType == .read
|
||||
// }
|
||||
// }
|
||||
|
||||
func sortParametersDidChange() {
|
||||
// performBlockAndRestoreSelection {
|
||||
|
|
|
@ -38,8 +38,8 @@ struct TimelineView: View {
|
|||
.help(timelineModel.isReadFiltered ?? false ? "Show Read Articles" : "Filter Read Articles")
|
||||
}
|
||||
ScrollViewReader { scrollViewProxy in
|
||||
List(timelineItems, selection: $timelineModel.selectedArticleIDs) { timelineItem in
|
||||
let selected = timelineModel.selectedArticleIDs.contains(timelineItem.article.articleID)
|
||||
List(timelineItems, selection: $timelineModel.selectedTimelineItemIDs) { timelineItem in
|
||||
let selected = timelineModel.selectedTimelineItemIDs.contains(timelineItem.article.articleID)
|
||||
TimelineItemView(selected: selected, width: geometryReaderProxy.size.width, timelineItem: timelineItem)
|
||||
.background(TimelineItemFramePreferenceView(timelineItem: timelineItem))
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ struct TimelineView: View {
|
|||
timelineItemFrames[pref.articleID] = pref.frame
|
||||
}
|
||||
}
|
||||
.onChange(of: timelineModel.selectedArticleIDs) { selectedArticleIDs in
|
||||
.onChange(of: timelineModel.selectedTimelineItemIDs) { selectedArticleIDs in
|
||||
let proxyFrame = geometryReaderProxy.frame(in: .global)
|
||||
for articleID in selectedArticleIDs {
|
||||
if let itemFrame = timelineItemFrames[articleID] {
|
||||
|
@ -70,14 +70,14 @@ struct TimelineView: View {
|
|||
.navigationTitle(Text(verbatim: timelineModel.nameForDisplay))
|
||||
#else
|
||||
ScrollViewReader { scrollViewProxy in
|
||||
List(timelineModel.timelineItems) { timelineItem in
|
||||
List(timelineItems) { timelineItem in
|
||||
ZStack {
|
||||
let selected = timelineModel.selectedArticleID == timelineItem.article.articleID
|
||||
let selected = timelineModel.selectedTimelineItemID == timelineItem.article.articleID
|
||||
TimelineItemView(selected: selected, width: geometryReaderProxy.size.width, timelineItem: timelineItem)
|
||||
.background(TimelineItemFramePreferenceView(timelineItem: timelineItem))
|
||||
NavigationLink(destination: ArticleContainerView(),
|
||||
tag: timelineItem.article.articleID,
|
||||
selection: $timelineModel.selectedArticleID) {
|
||||
selection: $timelineModel.selectedTimelineItemID) {
|
||||
EmptyView()
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ struct TimelineView: View {
|
|||
timelineItemFrames[pref.articleID] = pref.frame
|
||||
}
|
||||
}
|
||||
.onChange(of: timelineModel.selectedArticleID) { selectedArticleID in
|
||||
.onChange(of: timelineModel.selectedTimelineItemID) { selectedArticleID in
|
||||
let proxyFrame = geometryReaderProxy.frame(in: .global)
|
||||
if let articleID = selectedArticleID, let itemFrame = timelineItemFrames[articleID] {
|
||||
if itemFrame.minY < proxyFrame.minY + 3 || itemFrame.maxY > proxyFrame.maxY - 3 {
|
||||
|
@ -98,6 +98,12 @@ struct TimelineView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onReceive(timelineModel.timelineItemsPublisher!) { items in
|
||||
// Animations crash on iPadOS right now
|
||||
// withAnimation {
|
||||
timelineItems = items
|
||||
// }
|
||||
}
|
||||
.navigationBarTitle(Text(verbatim: timelineModel.nameForDisplay), displayMode: .inline)
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -61,9 +61,9 @@ class ArticleViewController: UIViewController {
|
|||
view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor)
|
||||
])
|
||||
|
||||
selectedArticlesCancellable = sceneModel?.timelineModel.$selectedArticles.sink { [weak self] articles in
|
||||
self?.articles = articles
|
||||
}
|
||||
// selectedArticlesCancellable = sceneModel?.timelineModel.$selectedArticles.sink { [weak self] articles in
|
||||
// self?.articles = articles
|
||||
// }
|
||||
|
||||
let controller = createWebViewController(currentArticle, updateView: true)
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
|
|
|
@ -340,6 +340,10 @@
|
|||
51A8001324CA0FC700F41F1D /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8001124CA0FC700F41F1D /* Sink.swift */; };
|
||||
51A8001524CA0FEC00F41F1D /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8001424CA0FEC00F41F1D /* DemandBuffer.swift */; };
|
||||
51A8001624CA0FEC00F41F1D /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8001424CA0FEC00F41F1D /* DemandBuffer.swift */; };
|
||||
51A8002D24CC451500F41F1D /* ShareReplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8002C24CC451500F41F1D /* ShareReplay.swift */; };
|
||||
51A8002E24CC451600F41F1D /* ShareReplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8002C24CC451500F41F1D /* ShareReplay.swift */; };
|
||||
51A8005124CC453C00F41F1D /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8005024CC453C00F41F1D /* ReplaySubject.swift */; };
|
||||
51A8005224CC453C00F41F1D /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8005024CC453C00F41F1D /* ReplaySubject.swift */; };
|
||||
51A8FFED24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8FFEC24CA0CF400F41F1D /* WIthLatestFrom.swift */; };
|
||||
51A8FFEE24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A8FFEC24CA0CF400F41F1D /* WIthLatestFrom.swift */; };
|
||||
51A9A5E12380C4FE0033AADF /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45255226507D200C03939 /* AppDefaults.swift */; };
|
||||
|
@ -2036,6 +2040,8 @@
|
|||
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = "<group>"; };
|
||||
51A8001124CA0FC700F41F1D /* Sink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sink.swift; sourceTree = "<group>"; };
|
||||
51A8001424CA0FEC00F41F1D /* DemandBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemandBuffer.swift; sourceTree = "<group>"; };
|
||||
51A8002C24CC451500F41F1D /* ShareReplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareReplay.swift; sourceTree = "<group>"; };
|
||||
51A8005024CC453C00F41F1D /* ReplaySubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = "<group>"; };
|
||||
51A8FFEC24CA0CF400F41F1D /* WIthLatestFrom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WIthLatestFrom.swift; sourceTree = "<group>"; };
|
||||
51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerAccountCell.xib; sourceTree = "<group>"; };
|
||||
51A9A5E52380C8B20033AADF /* ShareFolderPickerFolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerFolderCell.xib; sourceTree = "<group>"; };
|
||||
|
@ -3044,6 +3050,8 @@
|
|||
51A8001424CA0FEC00F41F1D /* DemandBuffer.swift */,
|
||||
51A8001124CA0FC700F41F1D /* Sink.swift */,
|
||||
51A8FFEC24CA0CF400F41F1D /* WIthLatestFrom.swift */,
|
||||
51A8002C24CC451500F41F1D /* ShareReplay.swift */,
|
||||
51A8005024CC453C00F41F1D /* ReplaySubject.swift */,
|
||||
);
|
||||
path = CombineExt;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4331,46 +4339,46 @@
|
|||
TargetAttributes = {
|
||||
51314636235A7BBE00387FDC = {
|
||||
CreatedOnToolsVersion = 11.2;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
LastSwiftMigration = 1120;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
513C5CE5232571C2003D4054 = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
518B2ED12351B3DD00400001 = {
|
||||
CreatedOnToolsVersion = 11.2;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 840D617B2029031C009BC708;
|
||||
};
|
||||
51C0513C24A77DF800194D5E = {
|
||||
CreatedOnToolsVersion = 12.0;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
51C0514324A77DF800194D5E = {
|
||||
CreatedOnToolsVersion = 12.0;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
6581C73220CED60000F4AD34 = {
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
65ED3FA2235DEF6C0081F399 = {
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
65ED4090235DEF770081F399 = {
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
840D617B2029031C009BC708 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
|
@ -4380,7 +4388,7 @@
|
|||
};
|
||||
849C645F1ED37A5D003D8FC0 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.HardenedRuntime = {
|
||||
|
@ -4390,7 +4398,7 @@
|
|||
};
|
||||
849C64701ED37A5D003D8FC0 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = FQLBNX3GP7;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 849C645F1ED37A5D003D8FC0;
|
||||
};
|
||||
|
@ -5241,6 +5249,7 @@
|
|||
65082A5224C72B88009FA994 /* SettingsCredentialsAccountModel.swift in Sources */,
|
||||
172199C924AB228900A31D04 /* SettingsView.swift in Sources */,
|
||||
51B8BCC224C25C3E00360B00 /* SidebarContextMenu.swift in Sources */,
|
||||
51A8005124CC453C00F41F1D /* ReplaySubject.swift in Sources */,
|
||||
17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
|
||||
51B80EB824BD1F8B00C6C32D /* ActivityViewController.swift in Sources */,
|
||||
51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
|
||||
|
@ -5308,6 +5317,7 @@
|
|||
51919FEE24AB85E400541E64 /* TimelineContainerView.swift in Sources */,
|
||||
653A4E7924BCA5BB00EF2D7F /* SettingsCloudKitAccountView.swift in Sources */,
|
||||
51E4995724A8734D00B667CB /* ExtensionPoint.swift in Sources */,
|
||||
51A8002D24CC451500F41F1D /* ShareReplay.swift in Sources */,
|
||||
51B8BCE624C25F7C00360B00 /* TimelineContextMenu.swift in Sources */,
|
||||
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
|
||||
51E4991124A808DE00B667CB /* SmallIconProvider.swift in Sources */,
|
||||
|
@ -5361,6 +5371,7 @@
|
|||
51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
|
||||
51B80F1F24BE531200C6C32D /* SharingServiceView.swift in Sources */,
|
||||
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
|
||||
51A8005224CC453C00F41F1D /* ReplaySubject.swift in Sources */,
|
||||
51E4993324A867E700B667CB /* AppNotifications.swift in Sources */,
|
||||
51B80F4224BE588200C6C32D /* SharingServicePickerDelegate.swift in Sources */,
|
||||
51E4990624A808C300B667CB /* ImageDownloader.swift in Sources */,
|
||||
|
@ -5418,6 +5429,7 @@
|
|||
514E6BDB24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */,
|
||||
51B8BCE724C25F7C00360B00 /* TimelineContextMenu.swift in Sources */,
|
||||
51E4996E24A8764C00B667CB /* ActivityManager.swift in Sources */,
|
||||
51A8002E24CC451600F41F1D /* ShareReplay.swift in Sources */,
|
||||
1769E33024BD6271000E1E8E /* EditAccountCredentialsView.swift in Sources */,
|
||||
51E4995A24A873F900B667CB /* ErrorHandler.swift in Sources */,
|
||||
5194737124BBCAF4001A2939 /* TimelineSortOrderView.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue