SwiftUIの@Stateや@AppStorageのようなものを自作する
タイトルの通り、@XXXを自作してみました
この機能は@propertyWrapperを使う事で自作が可能です
下は値をセットした時に新しい値をprintするだけのものになります
@propertyWrapper class DebugValue<Value> { var value: Value init(wrappedValue: Value) { value = wrappedValue } var wrappedValue: Value { get { return value } set { print(newValue) value = newValue } } }
使い方は下の通りです
ただ@Stateを使っていないので"count:(count)"の部分は自動で更新されません
struct ContentView: View { @DebugValue var count = 0 var body: some View { VStack { Button("count:\(count)", action: { count += 1 }) } .padding() } }
変更にViewに反映したい場合はDynamicPropertyに準拠してプロパティも@Stateで持つようにします
@propertyWrapper struct DebugValue<Value>: DynamicProperty { @State private var value: Value init(wrappedValue: Value) { value = wrappedValue } var wrappedValue: Value { get { return value } nonmutating set { print(newValue) value = newValue } } }
下のようにInt型に固定したり初期値をセットする事も可能です
@propertyWrapper struct DebugValue: DynamicProperty { @State private var value = 0 var wrappedValue: Int { get { return value } nonmutating set { print(newValue) value = newValue } } }
View側では下のように使います
struct ContentView: View { @DebugValue var count var body: some View { VStack { Button("count:\(count)", action: { count += 1 }) } .padding() } }
@AppStorageのようにUserDefaultsに保存したい場合は下のようにします
@propertyWrapper struct DebugValue<Value>: DynamicProperty { @State private var value: Value private let key: String init(wrappedValue: Value, _ key: String) { value = UserDefaults.standard.object(forKey: key) as? Value ?? wrappedValue self.key = key } var wrappedValue: Value { get { return value } nonmutating set { print(newValue) value = newValue UserDefaults.standard.set(newValue, forKey: key) } } }
StoreKit2での購入処理を試してみる
StoreKit2を使った購入処理を試してみました
商品情報の取得は下の通りです
Task {
let products = try? await Product.products(for: ["com.example"])
products?.forEach {
print($0)
}
}
実行すると下の形で商品情報を取得できます
{
"attributes" : {
"description" : {
"standard" : "説明文"
},
"editorialArtwork" : {
},
"icuLocale" : "ja_JP@currency=JPY",
"isFamilyShareable" : 0,
"isMerchandisedEnabled" : 0,
"isMerchandisedVisibleByDefault" : 0,
"isSubscription" : 0,
"kind" : "Non-Consumable",
"name" : "サンプル",
"offerName" : "com.example",
"offers" : [
{
"assets" : [
],
"buyParams" : "productType=A&price=300000&salableAdamId=XXXX&pricingParameters=STDQ&pg=default&offerName=com.example&appAdamId=XXXX",
"currencyCode" : "JPY",
"price" : 300,
"priceFormatted" : "¥300",
"priceString" : "300",
"type" : "buy"
}
],
"releaseDate" : "2009-06-17",
"supportsRuntimePricing" : 0,
"url" : "https:\/\/sandbox.itunes.apple.com\/jp\/app\/XXXX/idXXXX"
},
"href" : "\/v1\/catalog\/jp\/in-apps\/XXXXXX?l=ja",
"id" : "1197842797",
"type" : "in-apps"
}
購入処理は下の通りです
let result = try? await product.purchase() print(result)
購入結果は下のように取得できるので処理を書いていきます
switch result { case .success(let verificationResult): switch verificationResult { case .unverified(let transaction, let error): break case .verified(let transaction): await transaction.finish() } case .userCancelled: break case .pending: break @unknown default: break
過去の購入履歴は下のように取得できます
let transactions = Transaction.currentEntitlements for await transation in transactions { print(transation) }
iOS26のglassEffectを試してみる
iOS26でglassEffectというAPIを追加したので試してみました
こちらはSwiftUI専用のAPIのようで、UIKitではUIBlurEffectなどを使う事で似た状態を再現できます
まずは下のように画像とテキストが重なった状態にします
struct ContentView: View { var body: some View { ZStack { Image(.sample) Text("Hello, world!") } } }
アプリを起動すると下のような画面になります

Textを下のように全画面にしてglassEffectを追加します
Text("Hello, world!").frame(maxWidth: .infinity, maxHeight: .infinity).glassEffect()
実行すると下のような表示になります

glassEffectはtintで色を付ける事ができます
glassEffect(.regular.tint(.blue.opacity(0.3)))
実行結果は下の通りです

第一引数をデフォルトのregularからclearにするとぼかしのかからないガラスになります
あとはidentityというglassEffectを無効化するものもあります
glassEffect(.clear) glassEffect(.identity)
clearを選ぶと下のような表示になります
分かりにくいですが元画像より少しぼやけている感じになります

glassEffectは角丸がかかるのですが、第二引数にRectangleを渡すことで角丸をなくす事ができます
glassEffect(in: Rectangle())
結果は下の通りです

Run Scriptでoutputsがないという警告の対応をする
Xcodeで下のような警告が出ていたので調べみました
Showing Recent Issues Run script build phase 'Run Script(xassets)' will be run during every build because it does not specify any outputs. To address this issue, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase.
こちらはRun scriptのOutput Filesを指定していない事が原因です
Run scriptは入力ファイルと出力ファイルを見て実行するかどうか決定するのですが、ファイルを指定していない場合は警告が出ます
対策としては、Output Filesの出力と指定をして毎回実行されないようにする事があります

もしOutput Filesを見ずに毎回実行して良い場合はBased on dependency analysisのチェックを外せばファイルを見ずに毎回実行するようになります
ただチェックを外した場合は下のような警告が出るようになります
Showing Recent Issues Run script build phase 'Run Script' will be run during every build because the option to run the script phase "Based on dependency analysis" is unchecked.
Output Filesはないけど毎回実行してほしい時はFor install builds onlyのチェックを入れます
これを使うとアーカイブやインストールの時にだけ実行してくれるようになります
@unknown defaultの動作確認
Swift6から@unknown defaultが必須になったので調査しました
下のようなswitch文ですがSwift6からはSwitch covers known cases, but 'UIUserInterfaceStyle' may have additional unknown values, possibly added in future versionsというエラーになります
switch view.window!.windowScene!.screen.traitCollection.userInterfaceStyle { case .unspecified: print("unspecified") case .dark: print("dark") case .light: print("light") }
こちらは下のcaseを追加する事で解消します
@unknown default: print("@unknown default")
@unknown defaultについてですが、将来的にcaseが追加された時のための記述になります
将来OS側で.light、.darkに代わるモードが出た時も@unknown defaultがあれば対応できる想定です
@unknown defaultですが、これがある場合にcaseを省略するとSwitch must be exhaustiveという警告になります
switch view.window!.windowScene!.screen.traitCollection.userInterfaceStyle {
case .unspecified: print("unspecified")
case .dark: print("dark")
@unknown default: print("@unknown default")
}
defaultがある場合は@unknown defaultは不要です、逆に@unknown defaultを入れるとエラーになります
switch view.window!.windowScene!.screen.traitCollection.userInterfaceStyle { case .unspecified: print("unspecified") case .dark: print("dark") default: print("default") }
自分で定義したenumでは@unknown defaultは不要です
enum Test { case a case b case c } let test = Test.a switch test { case .a: print("a") case .b: print("b") case .c: print("c") }
外部ライブラリも下のように@frozenが付いている場合は@unknown defaultは入れなくて問題ありません
@frozen public enum Test { case a case b case c }
【UIKit】iOS26の画面真ん中スワイプで前画面に戻るので防ぐ
タイトルの通り、iOS26では画面の端以外をスワイプした場合も前の画面に戻る仕様になりました これを防ぎたい場合、下のようにinteractiveContentPopGestureRecognizerを無効にする必要があります
if #available(iOS 26.0, *) { navigationController?.interactiveContentPopGestureRecognizer?.isEnabled = false }
iOS26で登場したAlarmKitを試してみる
iOS26でAlarmKitという新しい通知が出たので試してみました
こちらの通知ですとおやすみモードでも通知を鳴らす事ができます
まずはInfo.plistにNSAlarmKitUsageDescriptionキーと利用理由を追加します
![]()
次は下のようにrequestAuthorizationで通知許可を求めます
import AlarmKit Task { let status = try? await AlarmManager.shared.requestAuthorization() print(status) }
requestAuthorizationを呼ぶと下のようなアラートが表示されます

アラームのセット方法は下の通りです
利用するクラス数が多いので慣れるまで大変そうでした
import AlarmKit struct MyMetadata: AlarmMetadata { } let scheduleFixed = Alarm.Schedule.fixed(Date(timeIntervalSinceNow: 5)) let alarmButton = AlarmButton(text: "button", textColor: .blue, systemImageName: "stop") let alert = AlarmPresentation.Alert(title: "テスト", stopButton: alarmButton) let alarmPresentation = AlarmPresentation(alert: alert) let attributes = AlarmAttributes<MyMetadata>(presentation: alarmPresentation, tintColor: .green) let alarmConfiguration = AlarmManager.AlarmConfiguration<MyMetadata>(schedule: scheduleFixed, attributes: attributes) Task { try? await AlarmManager.shared.schedule(id: UUID(), configuration: alarmConfiguration) }
アラームが鳴った様子は下の通りです
今回の実装ではiPhoneのタイマーと同じようにストップするまで鳴り続けました
iOS26.1で試した所、おやすみモードでも音付きで鳴る事が確認できました

今回は普通のアラームだけ試したのですが、カウントダウン方式やスヌーズ設定など様々な事ができるようです