-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Results history, Adaptive UI for Test runner, Settings Screen #31
base: master
Are you sure you want to change the base?
Conversation
- Remove old controller - localize the app - Reuse results view - Fix some minor bugs
@property (nonatomic, copy) NSString *hostname; | ||
@property (nonatomic) NSUInteger port; | ||
@property (nonatomic) NSUInteger duration; | ||
@property (nonatomic) NSUInteger streams; | ||
@property (nonatomic) IPFTestRunnerConfigurationType type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were made read-write so it can be easily configured from the view. The test runner class still gets a read only copy of it later when the test starts, so this change shouldn't be an issue. Let me know if you think otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are generated remodel value files:
https://github.com/facebook/remodel
They are immutable but have builders so you can mutate or create them. It is a bit of a hassle, and probably overkill for this little app, but I like the concept, and they're easily serializable for persistence too!
If you'd like to make changes, I would modify IPFTestRunnerConfiguration.value
and then run make
in that folder (you will have to install remodel
via npm first if I remember correctly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remodel seems to only generate immutable structures. With the form mutating the structure now whenever one of the configurations is tweaked (in the closures like onTextChange and onSegmentChange), it needs to be mutable. Agree that immutable models reduce future bugs, but since this is a config which is then passed into Test Runner (And screen is not editable while a test is running), I don't think there is a huge value add in going the remodel way. If serialization is a consideration for future, we should convert this to swift and make it Codable, so it can be easily converted using a JSONEncoder or PropertyListEncoder for storage.
view.backgroundColor = .white | ||
} | ||
|
||
form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Form and FormViewController are from a very light weight forms approach I created for building the UI). Its added under the UI/Support section of the project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these things are only used once I would prefer not to pull in extra code, and the style might look esoteric to other contributors, so I would stick to vanilla UI code here: can we just instantiate SwitchRow
and Section
instances, configure them and add them to the UI tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched the API to be non esoteric. Rows are added now via a standard function call
var results = [IPFTestResult]() | ||
|
||
func add(_ result: IPFTestResult) { | ||
results.insert(result, at: 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently just saving the results in-memory. I would like to use Core Data to allow a more robust and scalable persistence, and maybe iCloud sync using NSCloudPersistentContainer (iOS 13+). Majority of the devices should be on iOS 13 and plus. To use even NSPersistenContainer, we might have to drop iOS 9 from supported devices. Even for testing the UI currently, I could only find iOS 10.3.1 simulator embedded in Xcode. Let me know what you think on this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I've been a bit aggressive with supporting old iOS versions, I'll need to check what device usage looks like to know if iOS 9 can go or not. For persistence, I like the idea of iCloud syncing but we'd need to introduce a device tag to know which one made the individual tests, make sure they don't step on each other's toes...
Ideally we'd have an append-only file synced over iCloud, that way we reduce the possibility of conflicts. And given tests really are read only (you only ever add them or delete them) we could completely eliminate failure cases there. I remember there was an iCloud documents API, but I forgot exactly what that is called.
All of that would involve a fair bit of complexity, so just keeping the backing store a dumb local plist for now would probably be best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering the backwards compatibility the app needs, have used a dumb backing store for now. I used json though, based on some performance tests and data sizes. Have added some database tests in the test target but below is a summary of the results.
Large Dataset test: 10000 results
JSON advantages over Plist
File Size: 1.1MB vs 3.1MB - 1/3rd the file size
Save: 0.009seconds vs 0.018seconds - Half the time to save
Read: 0.086 vs 0.1 seconds - 14% faster read
Also JSON is more portable and understood by non-Apple platforms.
If you still want to keep Plist encoding instead, it is a small change and we just have to switch out the encoder-decoders.
INFOPLIST_FILE = "$(SRCROOT)/Resources/Info.plist"; | ||
IPHONEOS_DEPLOYMENT_TARGET = 9.3; | ||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; | ||
PRODUCT_BUNDLE_IDENTIFIER = com.teapotapps.iperf; | ||
PRODUCT_BUNDLE_IDENTIFIER = com.deepumukundan.iperf; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can revert these once the PR is fully reviewed. This helps me test in device quickly
Thanks for putting this up! Just want to make it clear it might take me a bit to review all of it, and specifically make sure the performance of the tests are not affected by the UI. This means a bit of profiling and profile-guided optimisations, watching Switch / Objective-C inter and so on. I also want to make sure we stay compatible with iOS as far as reasonable, so will need to review all the UIKit APIs we are using here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution! My overall impression is that that's quite a big chunk of code for just two screens, and we might be going a bit too generic on the forms. Plus I would rather move everything to Swift (except the thin Objective-C iPerf library interface) if we are going down that path.
As I mentioned before performance is also a big deal, so reducing the amount of code will help reduce the overhead on startup and during tests, I would love to see a before and after of binary size with all these changes.
Finally I try and have everything be defined by code so we don't get stuck having to redo all the assets should a @4x screen density show up, so the assets would have to be produced or rendered directly through CoreGraphics. Same for the remodel class, I would suggest reading up on how they work and embrace that way of doing things rather than exposing setters, and using remodel objects to serialism and persist data.
@property (nonatomic, copy) NSString *hostname; | ||
@property (nonatomic) NSUInteger port; | ||
@property (nonatomic) NSUInteger duration; | ||
@property (nonatomic) NSUInteger streams; | ||
@property (nonatomic) IPFTestRunnerConfigurationType type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are generated remodel value files:
https://github.com/facebook/remodel
They are immutable but have builders so you can mutate or create them. It is a bit of a hassle, and probably overkill for this little app, but I like the concept, and they're easily serializable for persistence too!
If you'd like to make changes, I would modify IPFTestRunnerConfiguration.value
and then run make
in that folder (you will have to install remodel
via npm first if I remember correctly).
"scale" : "2x" | ||
}, | ||
{ | ||
"filename" : "Results.png", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For images, I try and generate them with CoreGraphics so we don't have to embed lots of files and are always pixel perfect regardless of screen density, look at IPFIconGenerator.m
as an example. I also tend to reuse system icons as much as possible to go for a vanilla look, that way there is less maintenance for OS updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have replaced these ones with 3 pdf versions of system icons provided by SFSymbols.
Specifically used this tool to export 3 icons which seemed to match
https://github.com/davedelong/sfsymbols
Vector pdf's will scale to any size and have been around since Xcode 6, so should be backwards compatible to all OS versions supported by iPerf
view.backgroundColor = .white | ||
} | ||
|
||
form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these things are only used once I would prefer not to pull in extra code, and the style might look esoteric to other contributors, so I would stick to vanilla UI code here: can we just instantiate SwitchRow
and Section
instances, configure them and add them to the UI tree?
case enableSounds | ||
} | ||
|
||
public extension UserDefaults { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather have our own configuration object that then knows how to persist itself than have a tight coupling on UserDefaults
(and that class would just talk to NSUserDefaults
). What if we want to move to something else in the future? Testing would also be simpler that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we split this into another PR? I think the current PR as its own is huge.
Also this needs a little bit more hashing out. If we make the current IPFTestRunnerConfiguration into a swift Codable struct, we can get away with having to have tight coupling with user defaults and serialize and save the whole config in any way (JSON, Plist, UserDefaults, or something else). And even save the user's preference settings in there as those are also kind of a configuration for the test. But this will mean not using remodel.
Let me know
Got busy with work and life. I have fixed some of the comments above. Please review and resolve the ones which seem good. Will take a look at the pending ones once I have some time |
Most comments taken care of. Please check and let me know. I am pausing for a bit for you to review but in my local usage and tests everything looks good. |
Thanks for keeping at it! It might take a bit for me to go through this again, and I also have a pending iperf library update that I need to fix a couple bugs in, so please don't take it personally if this languishes a bit. What do you think about taking this as a separate branch until it all settles down? |
No worries. Take your time. It is serving my current needs of saving results and being able to track speeds over time, so all is good. Since my changes are on the fork, it should be as good as being in another branch right? |
@ndfred Updated with latest master. |
@ndfred Just checking back. Let me know if this is needed anymore or if long term plans for the UI have changed. |
This PR covers a lion's share of #30.
Things which are pending (The above changes were already overwhelming, so wanted to break things out)