simplified code
added large widgets
localised the counts
updated technotes
This commit is contained in:
Stuart Breckenridge 2020-11-18 15:43:14 +08:00
parent 1ed1b5df35
commit 264668a663
8 changed files with 142 additions and 165 deletions

View File

@ -85,6 +85,7 @@
17A1598624E3DEDD005DA32A /* RSDatabase in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 17A1598424E3DEDD005DA32A /* RSDatabase */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
17A1598824E3DEDD005DA32A /* RSParser in Frameworks */ = {isa = PBXBuildFile; productRef = 17A1598724E3DEDD005DA32A /* RSParser */; };
17A1598924E3DEDD005DA32A /* RSParser in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 17A1598724E3DEDD005DA32A /* RSParser */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
17D0682C2564F47E00C0B37E /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 17D0682B2564F47E00C0B37E /* Localizable.stringsdict */; };
17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; };
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; };
17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */; };
@ -1523,6 +1524,7 @@
1799E6CC24C320D600511E91 /* InspectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorModel.swift; sourceTree = "<group>"; };
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = "<group>"; };
17D0682B2564F47E00C0B37E /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = "<group>"; };
17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedModel.swift; sourceTree = "<group>"; };
17D5F17024B0BC6700375168 /* SidebarToolbarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbarModel.swift; sourceTree = "<group>"; };
17E4DBD524BFC53E00FE462A /* AdvancedPreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedPreferencesModel.swift; sourceTree = "<group>"; };
@ -2266,6 +2268,7 @@
176813F82564BB2C00D98635 /* Widget */ = {
isa = PBXGroup;
children = (
17D0683F2564F7AD00C0B37E /* Helpers */,
176814822564C02A00D98635 /* NetNewsWire_iOS_WidgetExtension.entitlements */,
176814122564BC8A00D98635 /* WidgetBundle.swift */,
1768142C2564BCA800D98635 /* TimelineProvider.swift */,
@ -2274,6 +2277,7 @@
176814792564BE3C00D98635 /* Resources */,
176813FB2564BB2D00D98635 /* Assets.xcassets */,
176813FD2564BB2D00D98635 /* Info.plist */,
17D0682B2564F47E00C0B37E /* Localizable.stringsdict */,
);
path = Widget;
sourceTree = "<group>";
@ -2385,6 +2389,13 @@
path = Add;
sourceTree = "<group>";
};
17D0683F2564F7AD00C0B37E /* Helpers */ = {
isa = PBXGroup;
children = (
);
path = Helpers;
sourceTree = "<group>";
};
510289CE2451BA1E00426DDF /* Twitter */ = {
isa = PBXGroup;
children = (
@ -4232,6 +4243,7 @@
files = (
176813FC2564BB2D00D98635 /* Assets.xcassets in Resources */,
1768147B2564BE5400D98635 /* widget-sample.json in Resources */,
17D0682C2564F47E00C0B37E /* Localizable.stringsdict in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -4475,7 +4487,7 @@
/* Begin PBXShellScriptBuildPhase section */
515D50802326D02600EE1167 /* Run Script: Verify No Build Settings */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 12;
buildActionMask = 8;
files = (
);
inputFileListPaths = (
@ -4487,7 +4499,7 @@
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBuildSettings.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n";
};

View File

@ -20,7 +20,6 @@ struct WidgetDataEncoder {
static func encodeWidgetData() {
os_log(.info, "Starting widget data encoding")
let task = UIApplication.shared.beginBackgroundTask(withName: taskIdentifier, expirationHandler: nil)
do {
// Unread Articles
let unreadArticles = try SmartFeedsController.shared.unreadFeed.fetchArticles().sorted(by: { $0.datePublished! > $1.datePublished! })
@ -33,7 +32,7 @@ struct WidgetDataEncoder {
feedIcon: article.iconImage()?.image.dataRepresentation(),
pubDate: article.datePublished!.description)
unread.append(latestArticle)
if unread.count == 3 { break }
if unread.count == 8 { break }
}
// Starred Articles
@ -47,7 +46,7 @@ struct WidgetDataEncoder {
feedIcon: article.iconImage()?.image.dataRepresentation(),
pubDate: article.datePublished!.description)
starred.append(latestArticle)
if starred.count == 3 { break }
if starred.count == 8 { break }
}
// Today Articles
@ -61,7 +60,7 @@ struct WidgetDataEncoder {
feedIcon: article.iconImage()?.image.dataRepresentation(),
pubDate: article.datePublished!.description)
today.append(latestArticle)
if today.count == 3 { break }
if today.count == 8 { break }
}
let latestData = WidgetData(currentUnreadCount: SmartFeedsController.shared.unreadFeed.unreadCount,
@ -80,13 +79,11 @@ struct WidgetDataEncoder {
try FileManager.default.removeItem(at: dataURL!)
}
try encodedData.write(to: dataURL!)
print(latestData.unreadArticles.count)
WidgetCenter.shared.reloadAllTimelines()
os_log(.info, "Finished encoding widget data")
UIApplication.shared.endBackgroundTask(task)
} catch {
os_log(.error, "%@", error.localizedDescription)
UIApplication.shared.endBackgroundTask(task)
}
}

View File

@ -1,9 +1,10 @@
# Widgets on iOS
There are _currently_ four widgets available for iOS:
There are _currently_ seven widgets available for iOS:
- 1x small widget that displays the current count of each of the Smart Feeds.
- 1x small widget that displays the current count of each of the Smart Feeds
- 3x medium widgets—one for each of the smart feeds.
- 3x large widgets—bigger versions of the medium widgets
## Widget Data
The widget does not have access to the parent app's database. To surface data to the widget, a small amount of article data is encoded to JSON (see `WidgetDataEncoder`) and saved to the AppGroup container.

View File

@ -0,0 +1,60 @@
<?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>UnreadCount</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@unread_count@</string>
<key>unread_count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>zero</key>
<string>No more unread articles</string>
<key>one</key>
<string>+ 1 more unread article</string>
<key>other</key>
<string>+ %u more unread articles</string>
</dict>
</dict>
<key>StarredCount</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@starred_count@</string>
<key>starred_count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>zero</key>
<string>No more starred articles</string>
<key>one</key>
<string>+ 1 more starred article</string>
<key>other</key>
<string>+ %u more starred articles</string>
</dict>
</dict>
<key>TodayCount</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@today_count@</string>
<key>today_count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>u</string>
<key>zero</key>
<string>No more recent articles</string>
<key>one</key>
<string>+ 1 more recent article</string>
<key>other</key>
<string>+ %u more recent articles</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -16,20 +16,6 @@ struct StarredWidgetView : View {
var entry: Provider.Entry
var body: some View {
switch family {
case .systemSmall:
mediumWidget
case .systemMedium:
mediumWidget
case .systemLarge:
mediumWidget
@unknown default:
mediumWidget
}
}
@ViewBuilder
var mediumWidget: some View {
if entry.widgetData.starredArticles.count == 0 {
inboxZero
}
@ -49,7 +35,7 @@ struct StarredWidgetView : View {
Spacer()
HStack {
Spacer()
unreadCountText
countText
}
}
}
@ -75,43 +61,28 @@ struct StarredWidgetView : View {
.cornerRadius(4)
}
var unreadCountText: some View {
if entry.widgetData.currentStarredCount > 3 {
let count = entry.widgetData.currentStarredCount - 3
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: count))
var str = ""
if count == 1 {
str = "+ \(formattedCount!) more starred article..."
} else {
str = "+ \(formattedCount!) more starred articles..."
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
var countText: some View {
var count = entry.widgetData.currentStarredCount
if family == .systemLarge {
count = count - 8
} else {
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: entry.widgetData.currentStarredCount))
var str = ""
if entry.widgetData.currentStarredCount == 1 {
str = "\(formattedCount!) starred article"
} else {
str = "\(formattedCount!) starred articles"
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
count = count - 3
}
if count < 0 { count = 0 }
let formatString = NSLocalizedString("StarredCount",
comment: "Starred Count Format")
let str = String.localizedStringWithFormat(formatString, UInt(count))
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
}
func maxCount() -> Int {
return entry.widgetData.starredArticles.count > 3 ? 3 : entry.widgetData.starredArticles.count
if family == .systemLarge {
return entry.widgetData.currentStarredCount > 8 ? 8 : entry.widgetData.currentStarredCount
}
return entry.widgetData.currentStarredCount > 3 ? 3 : entry.widgetData.currentStarredCount
}
var inboxZero: some View {
@ -120,8 +91,6 @@ struct StarredWidgetView : View {
Text("#StarredZero")
.italic()
.font(Font.system(.subheadline, design: .serif))
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 4)
Spacer()
HStack {
@ -133,7 +102,7 @@ struct StarredWidgetView : View {
Text("You've not starred any artices.")
.font(.caption2)
.foregroundColor(.gray)
}.padding(.bottom, 8)
}
}.padding()
}

View File

@ -16,20 +16,6 @@ struct TodayWidgetView : View {
var entry: Provider.Entry
var body: some View {
switch family {
case .systemSmall:
mediumWidget
case .systemMedium:
mediumWidget
case .systemLarge:
mediumWidget
@unknown default:
mediumWidget
}
}
@ViewBuilder
var mediumWidget: some View {
if entry.widgetData.todayArticles.count == 0 {
inboxZero
}
@ -49,7 +35,7 @@ struct TodayWidgetView : View {
Spacer()
HStack {
Spacer()
unreadCountText
countText
}
}
}
@ -75,42 +61,27 @@ struct TodayWidgetView : View {
.cornerRadius(4)
}
var unreadCountText: some View {
if entry.widgetData.currentTodayCount > 3 {
let count = entry.widgetData.currentTodayCount - 3
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: count))
var str = ""
if count == 1 {
str = "+ \(formattedCount!) more recent article..."
} else {
str = "+ \(formattedCount!) more recent articles..."
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
var countText: some View {
var count = entry.widgetData.currentTodayCount
if family == .systemLarge {
count = count - 8
} else {
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: entry.widgetData.currentTodayCount))
var str = ""
if entry.widgetData.currentTodayCount == 1 {
str = "\(formattedCount!) recent article"
} else {
str = "\(formattedCount!) recent articles"
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
count = count - 3
}
if count < 0 { count = 0 }
let formatString = NSLocalizedString("TodayCount",
comment: "Today Count Format")
let str = String.localizedStringWithFormat(formatString, UInt(count))
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
}
func maxCount() -> Int {
if family == .systemLarge {
return entry.widgetData.todayArticles.count > 8 ? 8 : entry.widgetData.todayArticles.count
}
return entry.widgetData.todayArticles.count > 3 ? 3 : entry.widgetData.todayArticles.count
}
@ -120,8 +91,6 @@ struct TodayWidgetView : View {
Text("#TodayZero")
.italic()
.font(Font.system(.subheadline, design: .serif))
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 4)
Spacer()
HStack {
@ -130,10 +99,10 @@ struct TodayWidgetView : View {
.frame(width: 15, height: 15, alignment: .center)
.cornerRadius(4)
Text("There're no recent articles to read.")
Text("There are no recent articles to read.")
.font(.caption2)
.foregroundColor(.gray)
}.padding(.bottom, 8)
}
}.padding()
}

View File

@ -16,21 +16,7 @@ struct UnreadWidgetView : View {
var entry: Provider.Entry
var body: some View {
switch family {
case .systemSmall:
mediumWidget
case .systemMedium:
mediumWidget
case .systemLarge:
mediumWidget
@unknown default:
mediumWidget
}
}
@ViewBuilder
var mediumWidget: some View {
if entry.widgetData.unreadArticles.count == 0 {
if entry.widgetData.currentUnreadCount == 0 {
inboxZero
}
else {
@ -49,13 +35,13 @@ struct UnreadWidgetView : View {
Spacer()
HStack {
Spacer()
unreadCountText
countText
}
}
}
}
.padding()
.widgetURL(URL(string: "nnw-widget://showunread")!)
.widgetURL(WidgetDeepLink.unread.url)
}
}
@ -75,42 +61,27 @@ struct UnreadWidgetView : View {
.cornerRadius(4)
}
var unreadCountText: some View {
if entry.widgetData.currentUnreadCount > 3 {
let count = entry.widgetData.currentUnreadCount - 3
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: count))
var str = ""
if count == 1 {
str = "+ \(formattedCount!) more unread article..."
} else {
str = "+ \(formattedCount!) more unread articles..."
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
var countText: some View {
var count = entry.widgetData.currentUnreadCount
if family == .systemLarge {
count = count - 8
} else {
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
let formattedCount = formatter.string(from: NSNumber(value: entry.widgetData.currentUnreadCount))
var str = ""
if entry.widgetData.currentUnreadCount == 1 {
str = "\(formattedCount!) unread article"
} else {
str = "\(formattedCount!) unread articles"
}
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
count = count - 3
}
if count < 0 { count = 0 }
let formatString = NSLocalizedString("UnreadCount",
comment: "Unread Count Format")
let str = String.localizedStringWithFormat(formatString, UInt(count))
return Text(str)
.font(.caption2)
.bold()
.foregroundColor(.accentColor)
}
func maxCount() -> Int {
if family == .systemLarge {
return entry.widgetData.unreadArticles.count > 8 ? 8 : entry.widgetData.unreadArticles.count
}
return entry.widgetData.unreadArticles.count > 3 ? 3 : entry.widgetData.unreadArticles.count
}
@ -120,8 +91,6 @@ struct UnreadWidgetView : View {
Text("#UnreadZero")
.italic()
.font(Font.system(.subheadline, design: .serif))
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 4)
Spacer()
HStack {
@ -133,7 +102,7 @@ struct UnreadWidgetView : View {
Text("There's nothing to read right now.")
.font(.caption2)
.foregroundColor(.gray)
}.padding(.bottom, 8)
}
}.padding()
}

View File

@ -24,7 +24,7 @@ struct UnreadWidget: Widget {
})
.configurationDisplayName("Your Unread Articles")
.description("A sneak peak at what's left unread.")
.supportedFamilies([.systemMedium])
.supportedFamilies([.systemMedium, .systemLarge])
}
}
@ -42,7 +42,7 @@ struct TodayWidget: Widget {
})
.configurationDisplayName("Your Today Articles")
.description("A sneak peak at recently published articles.")
.supportedFamilies([.systemMedium])
.supportedFamilies([.systemMedium, .systemLarge])
}
}
@ -60,7 +60,7 @@ struct StarredWidget: Widget {
})
.configurationDisplayName("Your Starred Articles")
.description("A sneak peak at your starred articles.")
.supportedFamilies([.systemMedium])
.supportedFamilies([.systemMedium, .systemLarge])
}
}