I’ve decided to start live streaming the development of my iOS applications.
Currently working on a “Dog Camera” Application.
You can find these live streams at https://www.youtube.com/@JJAllenTech.
A developer's rambling
I’ve decided to start live streaming the development of my iOS applications.
Currently working on a “Dog Camera” Application.
You can find these live streams at https://www.youtube.com/@JJAllenTech.
I’ve release the bowling score tracker app to the Apple App Store. Find it at the link below.
https://apps.apple.com/us/app/bowling-score-tracker-keeper/id6443573052
If you feel so inclined or wish to see more development on it, buys some of the in app purchases. They support the development efforts. Thanks.
When testing StoreKit connected directly to XCode, you need to have your .storekit
file open and then use the debug menu to test purchases.
This is the first time I realized that the menus changed what they have in them dependent on what file you have open. This also means when you go under Editor menu that also changes too.
This caused me to go in circles on why clearing Tester purchases in App Store Connect wasn’t working.
My girlfriend is part of a bowling league and every week she writes her scores down on a piece of paper. After seeing her do this for many seasons of bowling I’ve decided I would build her a simple mobile application that she can enter these scores into.
If you have a iPhone with iOS 15.6 or higher you can direct message me on twitter (with your email address, and first and last name) and I’ll invite you to the TestFlight. I’m planning on running this TestFlight beta until mid November 2022.
When you have to use UserDefaults to share data between a core app and a widget you have to do a couple of things.
Inside of the project Signing & Capabilities use the Add Capabilities
button to add App Group
. You need to do this while selecting both the app target and the widget target.
extension UserDefaults {
static let custom: UserDefaults = {
let appGroupId = "group.tech.justins.BowlingScoreTracker"
return UserDefaults(suiteName: appGroupId)!
}()
}
Now anywhere in the app where you set or get from UserDefaults just use .custom
instead of .default
. And that will synchronize across both of them.
Apple’s built in stepper allows you to only have one step increment value, I’ve designed on that allows for two different increment values. Initial version supported 1 and 10 for the step values and is tied to an integer.
import SwiftUI
struct ContentView: View {
@State var score: Int = 0
var min: Int = 0
var max: Int = 100
var template: String = "Score: %@"
var body: some View {
Form {
Text("Expanded Stepper Example")
HStack {
Text(String(format: template, score.formatted()))
Spacer()
Button("-10") {
score = score - 10
}
.disabled(score - 10 < min)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("-1") {
score = score - 1
}
.disabled(score - 1 < min)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("+1") {
score = score + 1
}
.disabled(score + 1 > max)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("+10") {
score = score + 10
}
.disabled(score + 10 > max)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This of course is the first revision, and it isn’t very reusable in the current state. So the next step was to make it more reusable.
import SwiftUI
struct AdvancedStepper: View {
var value: Binding<Int>
var min: Int
var max: Int
var template: String
var body: some View {
HStack {
Text(String(format: template, value.wrappedValue.formatted()))
Spacer()
Button("-10") {
value.wrappedValue = value.wrappedValue - 10
}
.disabled(value.wrappedValue - 10 < min)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("-1") {
value.wrappedValue = value.wrappedValue - 1
}
.disabled(value.wrappedValue - 1 < min)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("+1") {
value.wrappedValue = value.wrappedValue + 1
}
.disabled(value.wrappedValue + 1 > max)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
Button("+10") {
value.wrappedValue = value.wrappedValue + 10
}
.disabled(value.wrappedValue + 10 > max)
.buttonStyle(BorderlessButtonStyle())
.padding(.horizontal, 4)
}
}
}
struct ContentView: View {
@State var score: Int = 0
var body: some View {
Form {
Text("Expanded Stepper Example")
AdvancedStepper(value: $score, min: 0, max: 10, template: "Score: %@")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I think I’m happy enough with this one for now. Of course I could take this further and make the step amounts customizable by the usage location.
When building a page with a Linear Progress bar, I thought it should have been partially filled and it wasn’t filled at all. Turns out it expected value to be between 0-100 and I was assuming it would have been 0-1. Best to check the documentation over just guessing, but we all know developers hate reading the documentation. So best do some tests to see which range it expects.
Check that you are on the expected GIT Branch and GIT Commit!!
When you want to debug certain sections of code with in an environment without restarting Apache or NGINX, there’s a template I use for displaying print messages.
ini_set('display_errors', 1);
echo "<pre>".json_encode($VALUE, JSON_PRETTY_PRINT)."</pre>";
ini_set('display_errors', 0);
When you use Measurement format function in Swift it will always format the temperature to the format of the locale of the device. This is not the experience I wanted in my application. I wanted to provide the user an option to chose which scale they wanted to use, so I had to override the format operation. I used the following function to make this happen.
func formatTemperature(_ temperature: Double, _ scale: UnitTemperature) -> String {
let source = Measurement(value:temperature, unit: UnitTemperature.celsius)
let converted = source.converted(to: scale)
return "\(String(format: "%.1f", converted.value))\(converted.unit.symbol)"
}