Browse Source

very nice

batman
boB Rudis 4 years ago
parent
commit
ae90a9aa11
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 74
      F5 Weather.xcodeproj/project.pbxproj
  2. 2
      F5 Weather.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  3. 0
      F5 Weather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  4. 8
      F5 Weather.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  5. 20
      F5 Weather.xcodeproj/project.xcworkspace/xcuserdata/hrbrmstr.xcuserdatad/WorkspaceSettings.xcsettings
  6. 18
      F5 Weather.xcodeproj/xcshareddata/xcschemes/f5-weather.xcscheme
  7. 2
      F5 Weather.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/xcschememanagement.plist
  8. 58
      blah/Assets.xcassets/AppIcon.appiconset/Contents.json
  9. 6
      blah/Assets.xcassets/Contents.json
  10. 177
      blah/ContentView.swift
  11. 17
      blah/blahApp.swift
  12. 19746
      blah/emoji.swift
  13. 99
      blah/string+emoji.swift
  14. 4
      f5-weather/Assets.xcassets/AccentColor.colorset/Contents.json
  15. 68
      f5-weather/Assets.xcassets/AppIcon.appiconset/Contents.json
  16. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
  17. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
  18. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
  19. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
  20. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
  21. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
  22. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
  23. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
  24. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
  25. BIN
      f5-weather/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
  26. 6
      f5-weather/Assets.xcassets/Contents.json
  27. 64
      f5-weather/ContentView.swift
  28. 0
      f5-weather/Info.plist
  29. 0
      f5-weather/Preview Content/Preview Assets.xcassets/Contents.json
  30. 0
      f5-weather/f5-weather.entitlements
  31. 29
      f5-weather/f5wxApp.swift
  32. 28
      f5-weather/globals.swift
  33. 0
      f5-weather/json-utils.swift
  34. 92
      f5-weather/model.swift
  35. 32
      f5-weather/utils.swift

74
blah.xcodeproj/project.pbxproj → F5 Weather.xcodeproj/project.pbxproj

@ -7,9 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
010B0E7B2549EF5E007385A6 /* emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B0E792549EF5E007385A6 /* emoji.swift */; };
010B0E7C2549EF5E007385A6 /* string+emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B0E7A2549EF5E007385A6 /* string+emoji.swift */; };
015AB416254993C200AB0EF9 /* blahApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015AB415254993C200AB0EF9 /* blahApp.swift */; };
010B0E802549F642007385A6 /* utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B0E7F2549F642007385A6 /* utils.swift */; };
010B0E832549F65E007385A6 /* model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B0E822549F65E007385A6 /* model.swift */; };
010B0E862549F6A6007385A6 /* globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B0E852549F6A6007385A6 /* globals.swift */; };
015AB416254993C200AB0EF9 /* f5wxApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015AB415254993C200AB0EF9 /* f5wxApp.swift */; };
015AB418254993C200AB0EF9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015AB417254993C200AB0EF9 /* ContentView.swift */; };
015AB41A254993C200AB0EF9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 015AB419254993C200AB0EF9 /* Assets.xcassets */; };
015AB41D254993C200AB0EF9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 015AB41C254993C200AB0EF9 /* Preview Assets.xcassets */; };
@ -17,15 +18,16 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
010B0E792549EF5E007385A6 /* emoji.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = emoji.swift; sourceTree = "<group>"; };
010B0E7A2549EF5E007385A6 /* string+emoji.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "string+emoji.swift"; sourceTree = "<group>"; };
015AB412254993C100AB0EF9 /* blah.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = blah.app; sourceTree = BUILT_PRODUCTS_DIR; };
015AB415254993C200AB0EF9 /* blahApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = blahApp.swift; sourceTree = "<group>"; };
010B0E7F2549F642007385A6 /* utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = utils.swift; sourceTree = "<group>"; };
010B0E822549F65E007385A6 /* model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = model.swift; sourceTree = "<group>"; };
010B0E852549F6A6007385A6 /* globals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = globals.swift; sourceTree = "<group>"; };
015AB412254993C100AB0EF9 /* F5 Weather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "F5 Weather.app"; sourceTree = BUILT_PRODUCTS_DIR; };
015AB415254993C200AB0EF9 /* f5wxApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = f5wxApp.swift; sourceTree = "<group>"; };
015AB417254993C200AB0EF9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
015AB419254993C200AB0EF9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
015AB41C254993C200AB0EF9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
015AB41E254993C200AB0EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
015AB41F254993C200AB0EF9 /* blah.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = blah.entitlements; sourceTree = "<group>"; };
015AB41F254993C200AB0EF9 /* f5-weather.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "f5-weather.entitlements"; sourceTree = "<group>"; };
015AB4262549943B00AB0EF9 /* json-utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "json-utils.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -43,7 +45,7 @@
015AB409254993C100AB0EF9 = {
isa = PBXGroup;
children = (
015AB414254993C100AB0EF9 /* blah */,
015AB414254993C100AB0EF9 /* f5-weather */,
015AB413254993C100AB0EF9 /* Products */,
);
sourceTree = "<group>";
@ -51,25 +53,26 @@
015AB413254993C100AB0EF9 /* Products */ = {
isa = PBXGroup;
children = (
015AB412254993C100AB0EF9 /* blah.app */,
015AB412254993C100AB0EF9 /* F5 Weather.app */,
);
name = Products;
sourceTree = "<group>";
};
015AB414254993C100AB0EF9 /* blah */ = {
015AB414254993C100AB0EF9 /* f5-weather */ = {
isa = PBXGroup;
children = (
010B0E792549EF5E007385A6 /* emoji.swift */,
010B0E7A2549EF5E007385A6 /* string+emoji.swift */,
015AB4262549943B00AB0EF9 /* json-utils.swift */,
015AB415254993C200AB0EF9 /* blahApp.swift */,
015AB415254993C200AB0EF9 /* f5wxApp.swift */,
015AB417254993C200AB0EF9 /* ContentView.swift */,
015AB419254993C200AB0EF9 /* Assets.xcassets */,
015AB41E254993C200AB0EF9 /* Info.plist */,
015AB41F254993C200AB0EF9 /* blah.entitlements */,
015AB41F254993C200AB0EF9 /* f5-weather.entitlements */,
015AB41B254993C200AB0EF9 /* Preview Content */,
010B0E7F2549F642007385A6 /* utils.swift */,
010B0E822549F65E007385A6 /* model.swift */,
010B0E852549F6A6007385A6 /* globals.swift */,
);
path = blah;
path = "f5-weather";
sourceTree = "<group>";
};
015AB41B254993C200AB0EF9 /* Preview Content */ = {
@ -83,9 +86,9 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
015AB411254993C100AB0EF9 /* blah */ = {
015AB411254993C100AB0EF9 /* F5 Weather */ = {
isa = PBXNativeTarget;
buildConfigurationList = 015AB422254993C200AB0EF9 /* Build configuration list for PBXNativeTarget "blah" */;
buildConfigurationList = 015AB422254993C200AB0EF9 /* Build configuration list for PBXNativeTarget "F5 Weather" */;
buildPhases = (
015AB40E254993C100AB0EF9 /* Sources */,
015AB40F254993C100AB0EF9 /* Frameworks */,
@ -95,9 +98,9 @@
);
dependencies = (
);
name = blah;
name = "F5 Weather";
productName = blah;
productReference = 015AB412254993C100AB0EF9 /* blah.app */;
productReference = 015AB412254993C100AB0EF9 /* F5 Weather.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@ -114,7 +117,7 @@
};
};
};
buildConfigurationList = 015AB40D254993C100AB0EF9 /* Build configuration list for PBXProject "blah" */;
buildConfigurationList = 015AB40D254993C100AB0EF9 /* Build configuration list for PBXProject "F5 Weather" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -127,7 +130,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
015AB411254993C100AB0EF9 /* blah */,
015AB411254993C100AB0EF9 /* F5 Weather */,
);
};
/* End PBXProject section */
@ -150,10 +153,11 @@
buildActionMask = 2147483647;
files = (
015AB418254993C200AB0EF9 /* ContentView.swift in Sources */,
015AB416254993C200AB0EF9 /* blahApp.swift in Sources */,
010B0E7B2549EF5E007385A6 /* emoji.swift in Sources */,
015AB416254993C200AB0EF9 /* f5wxApp.swift in Sources */,
010B0E802549F642007385A6 /* utils.swift in Sources */,
010B0E862549F6A6007385A6 /* globals.swift in Sources */,
015AB4272549943B00AB0EF9 /* json-utils.swift in Sources */,
010B0E7C2549EF5E007385A6 /* string+emoji.swift in Sources */,
010B0E832549F65E007385A6 /* model.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -280,20 +284,20 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = blah/blah.entitlements;
CODE_SIGN_ENTITLEMENTS = "f5-weather/f5-weather.entitlements";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_ASSET_PATHS = "\"blah/Preview Content\"";
DEVELOPMENT_ASSET_PATHS = "\"f5-weather/Preview Content\"";
DEVELOPMENT_TEAM = CBY22P58G8;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = NO;
INFOPLIST_FILE = blah/Info.plist;
INFOPLIST_FILE = "f5-weather/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = is.rud.blah;
PRODUCT_BUNDLE_IDENTIFIER = "is.rud.f5-weather";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
@ -304,20 +308,20 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = blah/blah.entitlements;
CODE_SIGN_ENTITLEMENTS = "f5-weather/f5-weather.entitlements";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_ASSET_PATHS = "\"blah/Preview Content\"";
DEVELOPMENT_ASSET_PATHS = "\"f5-weather/Preview Content\"";
DEVELOPMENT_TEAM = CBY22P58G8;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = NO;
INFOPLIST_FILE = blah/Info.plist;
INFOPLIST_FILE = "f5-weather/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = is.rud.blah;
PRODUCT_BUNDLE_IDENTIFIER = "is.rud.f5-weather";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
@ -326,7 +330,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
015AB40D254993C100AB0EF9 /* Build configuration list for PBXProject "blah" */ = {
015AB40D254993C100AB0EF9 /* Build configuration list for PBXProject "F5 Weather" */ = {
isa = XCConfigurationList;
buildConfigurations = (
015AB420254993C200AB0EF9 /* Debug */,
@ -335,7 +339,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
015AB422254993C200AB0EF9 /* Build configuration list for PBXNativeTarget "blah" */ = {
015AB422254993C200AB0EF9 /* Build configuration list for PBXNativeTarget "F5 Weather" */ = {
isa = XCConfigurationList;
buildConfigurations = (
015AB423254993C200AB0EF9 /* Debug */,

2
blah.xcodeproj/project.xcworkspace/contents.xcworkspacedata → F5 Weather.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:">
location = "self:/Users/hrbrmstr/Development/blah/F5 Weather.xcodeproj">
</FileRef>
</Workspace>

0
blah.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist → F5 Weather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

8
F5 Weather.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@ -0,0 +1,8 @@
<?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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

20
F5 Weather.xcodeproj/project.xcworkspace/xcuserdata/hrbrmstr.xcuserdatad/WorkspaceSettings.xcsettings

@ -0,0 +1,20 @@
<?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>BuildLocationStyle</key>
<string>UseAppPreferences</string>
<key>CustomBuildLocationType</key>
<string>RelativeToDerivedData</string>
<key>DerivedDataCustomLocation</key>
<string>DerivedData</string>
<key>DerivedDataLocationStyle</key>
<string>WorkspaceRelativePath</string>
<key>IssueFilterStyle</key>
<string>ShowActiveSchemeOnly</string>
<key>LiveSourceIssuesEnabled</key>
<true/>
<key>ShowSharedSchemesAutomaticallyEnabled</key>
<true/>
</dict>
</plist>

18
blah.xcodeproj/xcshareddata/xcschemes/blah.xcscheme → F5 Weather.xcodeproj/xcshareddata/xcschemes/f5-weather.xcscheme

@ -15,9 +15,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "015AB411254993C100AB0EF9"
BuildableName = "blah.app"
BlueprintName = "blah"
ReferencedContainer = "container:blah.xcodeproj">
BuildableName = "F5 Weather.app"
BlueprintName = "F5 Weather"
ReferencedContainer = "container:F5 Weather.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@ -45,9 +45,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "015AB411254993C100AB0EF9"
BuildableName = "blah.app"
BlueprintName = "blah"
ReferencedContainer = "container:blah.xcodeproj">
BuildableName = "F5 Weather.app"
BlueprintName = "F5 Weather"
ReferencedContainer = "container:F5 Weather.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
@ -62,9 +62,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "015AB411254993C100AB0EF9"
BuildableName = "blah.app"
BlueprintName = "blah"
ReferencedContainer = "container:blah.xcodeproj">
BuildableName = "F5 Weather.app"
BlueprintName = "F5 Weather"
ReferencedContainer = "container:F5 Weather.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>

2
blah.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/xcschememanagement.plist → F5 Weather.xcodeproj/xcuserdata/hrbrmstr.xcuserdatad/xcschemes/xcschememanagement.plist

@ -4,7 +4,7 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>blah.xcscheme_^#shared#^_</key>
<key>f5-weather.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>

58
blah/Assets.xcassets/AppIcon.appiconset/Contents.json

@ -1,58 +0,0 @@
{
"images" : [
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

6
blah/Assets.xcassets/Contents.json

@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

177
blah/ContentView.swift

@ -1,177 +0,0 @@
//
// ContentView.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import SwiftUI
let DOMAIN_MIN: Double = 0
let DOMAIN_MAX: Double = 300
let HEIGHT: CGFloat = 20
let etab: [String: String] = [
"Rain" : ":cloud_rain:".emojiUnescapedString,
"Snow" : ":cloud_snow:".emojiUnescapedString,
"Clear" : ":sunny:".emojiUnescapedString,
]
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
}
}
}
struct ContentView: View {
@ObservedObject var model = AppModel()
var body: some View {
VStack {
List {
ForEach(model.readings) {
(r) in DayView(reading: r, min: model.min, max: model.max)
}
}.frame(width: 540, height: 330, alignment: .topLeading)
}
}
}
struct DayView: View {
let reading: F5DayCast
let min: Double
let max: Double
var body : some View {
HStack {
Text(reading.day.split(separator: " ")[0]).frame(width: 3*12, height: HEIGHT, alignment: .center)
Text(reading.day.split(separator: " ")[1]).frame(width: 3*12, height: HEIGHT, alignment: .center)
Text(etab[reading.condition, default: "Clear"]).frame(width: 2*12, height: HEIGHT, alignment: .center)
Spacer().frame(width: CGFloat(reading.low.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX)-0),
height: HEIGHT, alignment: .leading)//.background(Color.red)
Text(String(format: "%.f°F", reading.low)).frame(width: 3*12, height: HEIGHT, alignment: .trailing)
Spacer().frame(width: 10.0, height: HEIGHT, alignment: .leading)
GeometryReader { g in
Path { path in
let w = g.size.width
let h = g.size.height
path.move(to: CGPoint(x: 0, y: h/2))
path.addLine(to: CGPoint(x:w, y: h/2))
}.stroke(style: StrokeStyle(lineWidth: HEIGHT/2, lineCap: .round)).foregroundColor(Color.primary)
}.frame(width: CGFloat(reading.high.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX)-reading.low.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX)),
height: HEIGHT, alignment: .leading)
Spacer().frame(width: 10.0, height: HEIGHT, alignment: .leading)
Text(String(format: "%.f°F", reading.high)).frame(width: 3*12, height: HEIGHT, alignment: .leading)
}
}
}
struct F5DayCast: Identifiable {
var id = UUID()
var day: String = ""
var low: Double = 0.0
var high: Double = 0.0
var condition: String = ""
}
extension Double {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
extension FloatingPoint {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
extension BinaryInteger {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
class AppModel: NSObject, ObservableObject {
// {"V1":"Wed 10/28","V2":38,"V3":47,"conditions":"Rain","c_alpha":0.75}
@Published var readings: [F5DayCast] = []
@Published var min: Double = Double.infinity
@Published var max: Double = -Double.infinity
override init() {
super.init()
getReadings()
}
func getReadings() {
let url = URL(string: "https://tycho/data/f5.json")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
debugPrint("Error fetching data: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else { return }
if let mimeType = httpResponse.mimeType, mimeType == "application/json",
let data = data,
let res = String(data: data, encoding: .utf8) {
let lines = res.split(whereSeparator: \.isNewline)
self.readings = lines.map { line in
let v = try? JSONDecoder().decode(JSON.self, from: line.data(using: .utf8)!)
if let m = v!.V2.doubleValue {
self.min = (m < self.min) ? m : self.min
}
if let m = v!.V3.doubleValue {
self.max = (m > self.max) ? m : self.max
}
return(
F5DayCast(
day: v!.V1.stringValue ?? "ERROR",
low: v!.V2.doubleValue ?? -1.0,
high: v!.V3.doubleValue ?? -1.0,
condition: v!.conditions.stringValue ?? "ERROR"
)
)
}
debugPrint(24.rescale(from: self.min...self.max, to: 0...200))
debugPrint(57.rescale(from: self.min...self.max, to: 0...200))
debugPrint(30.rescale(from: self.min...self.max, to: 0...200))
}
}
task.resume()
}
}

17
blah/blahApp.swift

@ -1,17 +0,0 @@
//
// blahApp.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import SwiftUI
@main
struct blahApp: App {
var body: some Scene {
WindowGroup {
ContentView().navigationTitle("F5 Weather • ECMWF • Berwick, Maine")
}
}
}

19746
blah/emoji.swift

File diff suppressed because it is too large

99
blah/string+emoji.swift

@ -1,99 +0,0 @@
//
// string+emoji.swift
// cli2
//
// Created by boB Rudis on 10/28/20.
//
import Foundation
extension String {
fileprivate static var emojiUnescapeRegExp = createEmojiUnescapeRegExp()
fileprivate static var emojiEscapeRegExp = createEmojiEscapeRegExp()
fileprivate static var indexedShortnames = indexShortnames()
fileprivate static var indexedCodepoints = indexCodepoints()
fileprivate static func createEmojiUnescapeRegExp() -> NSRegularExpression? {
let v = Emoji.allCases.flatMap { $0.shortnames }
.map { ":\(NSRegularExpression.escapedPattern(for: $0)):" }
do {
let regex = try NSRegularExpression(pattern: v.joined(separator: "|"), options: [])
return regex
} catch {
print(error)
}
return nil
}
fileprivate static func createEmojiEscapeRegExp() -> NSRegularExpression? {
let v = Emoji.allCases.flatMap { $0.codepoints }
.map { NSRegularExpression.escapedPattern(for: $0) }
.sorted()
.reversed()
do {
let regex = try NSRegularExpression(pattern: v.joined(separator: "|"), options: [])
return regex
} catch {
print(error)
}
return nil
}
fileprivate static func indexShortnames() -> [String: Int] {
let emojis = Emoji.allCases
return emojis.reduce([String: Int](), { dict, emoji -> [String: Int] in
guard let index = emojis.firstIndex(of: emoji) else { return [:] }
return emoji.shortnames.reduce(dict, { eDict, shortname -> [String: Int] in
var finalDict = eDict
finalDict[shortname] = index
return finalDict
})
})
}
fileprivate static func indexCodepoints() -> [String: Int] {
let emojis = Emoji.allCases
return emojis.reduce([String: Int](), { dict, emoji -> [String: Int] in
guard let index = emojis.firstIndex(of: emoji) else { return [:] }
return emoji.codepoints.reduce(dict, { eDict, codepoint -> [String: Int] in
var finalDict = eDict
finalDict[codepoint] = index
return finalDict
})
})
}
public var emojiUnescapedString: String {
var s = self as NSString
let ms = String.emojiUnescapeRegExp?.matches(in: self, options: [], range: NSMakeRange(0, s.length))
ms?.reversed().forEach { m in
let r = m.range
let p = s.substring(with: r)
let px = p[p.index(after: p.startIndex) ..< p.index(before: p.endIndex)]
let index = String.indexedShortnames[String(px)]
if let i = index {
let e = Emoji.allCases[i]
s = s.replacingCharacters(in: r, with: e.codepoints.first!) as NSString
}
}
return s as String
}
public var emojiEscapedString: String {
var s = self as NSString
let ms = String.emojiEscapeRegExp?.matches(in: self, options: [], range: NSMakeRange(0, s.length))
ms?.reversed().forEach { m in
let r = m.range
let p = s.substring(with: r)
let index = String.indexedCodepoints[p]
if let i = index {
let e = Emoji.allCases[i]
s = s.replacingCharacters(in: r, with: ":\(e.shortnames.first!):") as NSString
}
}
return s as String
}
}

4
blah/Assets.xcassets/AccentColor.colorset/Contents.json → f5-weather/Assets.xcassets/AccentColor.colorset/Contents.json

@ -1,6 +1,10 @@
{
"colors" : [
{
"color" : {
"platform" : "osx",
"reference" : "findHighlightColor"
},
"idiom" : "universal"
}
],

68
f5-weather/Assets.xcassets/AppIcon.appiconset/Contents.json

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"scale" : "1x",
"filename" : "icon_16x16.png",
"idiom" : "mac"
},
{
"scale" : "2x",
"filename" : "icon_16x16@2x.png",
"idiom" : "mac",
"size" : "16x16"
},
{
"filename" : "icon_32x32.png",
"scale" : "1x",
"size" : "32x32",
"idiom" : "mac"
},
{
"filename" : "icon_32x32@2x.png",
"size" : "32x32",
"idiom" : "mac",
"scale" : "2x"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128",
"filename" : "icon_128x128.png"
},
{
"filename" : "icon_128x128@2x.png",
"scale" : "2x",
"size" : "128x128",
"idiom" : "mac"
},
{
"idiom" : "mac",
"filename" : "icon_256x256.png",
"size" : "256x256",
"scale" : "1x"
},
{
"filename" : "icon_256x256@2x.png",
"scale" : "2x",
"size" : "256x256",
"idiom" : "mac"
},
{
"filename" : "icon_512x512.png",
"scale" : "1x",
"size" : "512x512",
"idiom" : "mac"
},
{
"scale" : "2x",
"idiom" : "mac",
"size" : "512x512",
"filename" : "icon_512x512@2x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_128x128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_256x256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
f5-weather/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

6
f5-weather/Assets.xcassets/Contents.json

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

64
f5-weather/ContentView.swift

@ -0,0 +1,64 @@
//
// ContentView.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import SwiftUI
struct ContentView: View {
@EnvironmentObject var model: AppModel
var body: some View {
VStack {
List {
ForEach(model.readings) {
(r) in DayView(reading: r, min: model.min, max: model.max)
}
}
}.frame( minWidth: 528, idealWidth: 528, maxWidth: 528,
minHeight: 256, idealHeight: 330, maxHeight: 330)
}
}
struct DayView: View {
let reading: F5DayCast
let min: Double
let max: Double
var body : some View {
HStack {
Text(reading.day.split(separator: " ")[0]).frame(width: 3*12, height: HEIGHT, alignment: .center)
Text(reading.day.split(separator: " ")[1]).frame(width: 3*12, height: HEIGHT, alignment: .center)
Image(systemName: etab[reading.condition, default: "sun.max.fill"])
.renderingMode(.original)
.font(.title3)
.frame(width: 3*12, height: HEIGHT, alignment: .center)
.foregroundColor(ecol[reading.condition, default: Color.yellow])
Spacer().frame(width: CGFloat(reading.low.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX)-0),
height: HEIGHT, alignment: .leading)
Text(String(format: "%.f°", reading.low)).frame(width: 2*12, height: HEIGHT, alignment: .trailing)
Spacer().frame(width: 10.0, height: HEIGHT, alignment: .leading)
GeometryReader { g in
Path { path in
let w = g.size.width
let h = g.size.height
path.move(to: CGPoint(x: 0, y: h/2))
path.addLine(to: CGPoint(x:w, y: h/2))
}.stroke(style: StrokeStyle(lineWidth: HEIGHT/2, lineCap: .round)).foregroundColor(Color.primary)
}.frame(width: CGFloat(reading.high.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX) -
reading.low.rescale(from: self.min...self.max, to: DOMAIN_MIN...DOMAIN_MAX)),
height: HEIGHT, alignment: .leading)
Spacer().frame(width: 10.0, height: HEIGHT, alignment: .leading)
Text(String(format: "%.f°", reading.high)).frame(width: 2*12, height: HEIGHT, alignment: .leading)
}
}
}

0
blah/Info.plist → f5-weather/Info.plist

0
blah/Preview Content/Preview Assets.xcassets/Contents.json → f5-weather/Preview Content/Preview Assets.xcassets/Contents.json

0
blah/blah.entitlements → f5-weather/f5-weather.entitlements

29
f5-weather/f5wxApp.swift

@ -0,0 +1,29 @@
//
// blahApp.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import SwiftUI
var model = AppModel()
@main
struct blahApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.navigationTitle("F5 Weather • ECMWF • Berwick, Maine")
.environmentObject(model)
}.commands {
CommandMenu("Utilities") {
Button(action: {
model.getReadings()
}) {
Text("Refresh")
}.keyboardShortcut("r", modifiers: .command)
}
}
}
}

28
f5-weather/globals.swift

@ -0,0 +1,28 @@
//
// globals.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import Foundation
import SwiftUI
import os
let DOMAIN_MIN: Double = 0
let DOMAIN_MAX: Double = 300
let HEIGHT: CGFloat = 20
let logger = Logger()
let etab: [String: String] = [
"Rain" : "cloud.rain.fill",
"Snow" : "cloud.snow.fill",
"Clear" : "sun.max.fill"
]
let ecol: [String: Color] = [
"Rain" : Color.blue,
"Snow" : Color.blue,
"Clear" : Color.yellow
]

0
blah/json-utils.swift → f5-weather/json-utils.swift

92
f5-weather/model.swift

@ -0,0 +1,92 @@
//
// model.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import Foundation
struct F5DayCast: Identifiable {
var id = UUID()
var day: String = ""
var low: Double = 0.0
var high: Double = 0.0
var condition: String = ""
}
class AppModel: NSObject, ObservableObject {
// {"V1":"Wed 10/28","V2":38,"V3":47,"conditions":"Rain","c_alpha":0.75}
@Published var readings: [F5DayCast] = []
@Published var min: Double = Double.infinity
@Published var max: Double = -Double.infinity
override init() {
super.init()
getReadings()
}
func getReadings() {
let urlString = "https://rud.is/f5wx/conditions.json?q=\(Date().timeIntervalSince1970)"
logger.info("Retrieving \(urlString)")
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
debugPrint("Error fetching data: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else { return }
if let mimeType = httpResponse.mimeType, mimeType == "application/json",
let data = data,
let res = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
let lines = res.split(whereSeparator: \.isNewline)
self.readings = lines.map { line in
let v = try? JSONDecoder().decode(JSON.self, from: line.data(using: .utf8)!)
if let m = v!.V2.doubleValue {
self.min = (m < self.min) ? m : self.min
}
if let m = v!.V3.doubleValue {
self.max = (m > self.max) ? m : self.max
}
return(
F5DayCast(
day: v!.V1.stringValue ?? "ERROR",
low: v!.V2.doubleValue ?? -1.0,
high: v!.V3.doubleValue ?? -1.0,
condition: v!.conditions.stringValue ?? "ERROR"
)
)
}
}
}
}
task.resume()
}
}

32
f5-weather/utils.swift

@ -0,0 +1,32 @@
//
// utils.swift
// blah
//
// Created by hrbrmstr on 10/28/20.
//
import Foundation
extension Double {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
extension FloatingPoint {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
extension BinaryInteger {
func rescale(from input: ClosedRange<Self>, to output: ClosedRange<Self>) -> Self {
let x = (output.upperBound - output.lowerBound) * (self - input.lowerBound)
let y = (input.upperBound - input.lowerBound)
return x / y + output.lowerBound
}
}
Loading…
Cancel
Save