[Swift] 73 SwiftUIの特異な仕様 / 選択したカレンダーのイベントをForEach文で表示

[Mac M2 Pro 12CPU, Sonoma 14.3.1]

画像はwatchOSアプリで”日本の祝日”カレンダーの向こう2ヶ月分を表示させたものです。

海の日だけが表示されている普通の画面のように見えますが、実は背景色が黒の空イベント2件分で海の日の下を埋めています。

そうしないと選択していない他のカレンダーの繰り返しイベントが表示されてしまいます。仕様というかOSの完全なバグでしょう。

便利だからSwiftUIを使ってはいるものの、こういった現象に遭遇するとげんなりしますね。コード自体は間違っていないですから、原因究明が大変です。

[Swift] 72 フォントサイズの自動調整

[Mac M2 Pro 12CPU, Sonoma 14.3.1]

watchOSアプリでフォントサイズを自動調整する場合は、lineLimitモディファイアやminimumScaleFactorモディファイアを使用します。

HStack(spacing:4){
    Text("White Background")
        .font(.body)
        .frame(maxWidth: .infinity, alignment: .leading)
        .lineLimit(1)
        .minimumScaleFactor(0.8)
    Spacer()
    Toggle(isOn: $whiteBack) {
        Text("")
    }
        .labelsHidden()
        .onChange(of: whiteBack) { newValue in
            defaults?.set(newValue, forKey: whiteBackKey)
        }
}

[Swift] 71 条件演算子を使ったリファクタリング

[Mac M2 Pro 12CPU, Sonoma 14.3.1]

SwiftUIアプリでトグルボタン2つと言語設定で分岐させたif文がメンテ不能な程にややこしくなったため、条件演算子を使ってリファクタリングしました。

条件 ? 真の場合の値 : 偽の場合の値

Clangではあり得なかったことですが、Xcodeでややこしいコードを書くと根を上げてエラーになりリファクタリングを強制されます。

[整理前]

VStack (spacing: -6){
    if bezelDisplay {
        if !coloredCMP {
            if languageCode!.contains("ja") {
                Text(getFormattedWeekday(entry.date).prefix(2))
                    .font(.system(size: 15))
                    .bold()
                    .foregroundColor(Color(hex:colorCircular1Hex))
                    .containerBackground(for: .widget){
                        Color.blue
                    }
                    .widgetLabel {
                        Text(getYear(entry.date) + " " + getFormattedYear(entry.date))
                            .font(.system(size: 20))
                    }
            } else {
                Text(getFormattedWeekday(entry.date).prefix(2))
                    .font(.system(size: 15))
                    .bold()
                    .foregroundColor(Color(hex:colorCircular1Hex))
                    .containerBackground(for: .widget){
                        Color.blue
                    }
                    .widgetLabel {
                        Text(getYear(entry.date))
                            .font(.system(size: 20))
                    }
            }
        } else {

<以下略>

[整理後]

VStack (spacing: -6){
    let fontSize: Font = coloredCMP ? .system(size: 18) : .body
    let yearText: String = bezelDisplay ? (languageCode!.contains("ja") ? getYear(entry.date) + " " + getFormattedYear(entry.date) : getYear(entry.date)) : ""

    Text(getFormattedWeekday(entry.date))
        .font(fontSize)
        .bold()
        .foregroundColor(Color(hex: colorCircular1Hex))
        .containerBackground(for: .widget) {
            Color.blue
        }
        .padding(.vertical, 2)
        .widgetLabel {
            Text(yearText)
                .font(.system(size: 20))
        }
        
    Text(getDay(entry.date))
        .font(.system(size: 20))
        .bold()
        .foregroundColor(Color(hex:colorCircular3Hex))
        .containerBackground(for: .widget){
            Color.blue
        }
}

[Swift] 71 ローマ字を英数モードで打った時のリカバリー AppleScript

[Mac M2 Pro 12CPU, Sonoma 14.3.1]

ローマ字を英数モードで打ってしまった時に、かなキー2回押しでひらがなに変換するようにしました。SwiftとAppleScriptを組み合わせています。

ローマ字はヘボン式なのが難点です。”し”は”shi、”ち”はchiになります。また反応速度がやや遅く、入力モードを切り替えてすぐに入力すると最初の1文字を取りこぼします。

これでは常用できませんね。2回押しで一気に再変換まで出来るようにしたかったのですが、反応速度と合わせて今後の課題とします。

[AI] ローカルLLM検証アプリの製作 llama.cpp SwiftUI

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]

ローカルLLMの性能を検証するアプリをSwiftUIで製作しました。

modelsディレクトリに入っているggufファイルを選択し、promptを入力してtxtファイル化、llama.cppのmainコマンドを作成します。コマンドはクリップボードに自動コピーされます。あとはターミナルにコマンドを貼り付けて実行し、responseを手動で右側のTextEditorにペーストしてtxtファイルにします。

promptファイルとresponseファイルのプレフィックスはコマンド作成時のタイムスタンプ(yymmdd_hhmmss)になっています。

今回はほとんどclaude-3 opusとgpt-4にコードを考えてもらいました。Buttonラベルの改行表示についてはclaude-3では埒が開かず、gpt-4がテキスト分割を提案して不本意ながら解決しました。SwiftUIに関してはclaude-3とgpt-4はほぼ互角といった印象です。

SwiftUIのmacOS版ではButtonのラベルを改行できないようです。iOS版では問題なく出来るはずですが、macOS版の後進性に若干引いています。

※claude-3利用状況
claude-3のAPIキーを取得して5日経ち、5ドルの無料分がほぼ無くなりました。私の用途ではgpt-4に対する優位性を体感できなかったので、今後は割安なgpt-4メインに戻ります。なおclaude-3 APIの支払いで個人はマイナンバーの入力を求められているため、登録は見送りました。Claude Pro($20/month)への登録もしません。

[AI] ローカルLLM検証 CodeLlama系日本語学習モデル その3 Mac M2 Proで動作確認

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
実行方法:llama.cpp

記事その1ではWindows11PCで検証しましたが、GGUF形式であればMacでも動作可能なので早速試してみました。Metalを使用しています。

4bit量子化したGGUF形式のモデルはサイズが4.08GBですから、RAM16GBでも問題なさそうです。

質問によっては無回答で終了することもあるものの、それなりに考えたプロンプトであればSwiftUIの簡単なコードについては正しい答えが返ってきました。

ただし量子化の影響なのか、下図のような簡潔な正答になることもあれば、勝手にチャットのようになったり、誤答を返すことも多く、結構不安定です。

量子化していないGGUF形式のモデルで検証したいところです。

./main -m models/ELYZA-japanese-CodeLlama-7b-instruct-q4_K_M.gguf --temp 1.0 -ngl 1 -t 10 -f ./prompt_jp.txt

参考サイト

[AI] Claude 3 vs GPT-4 / Swiftプログラミング補助 24/03/12

話題のClaude 3 (Sonnet)を即席評価しました。

またもやGPT-4の圧勝でした。2月に評価したGeminiと同じく読解力が不足しています。なお最上級のOpusはサブスクかAPIでないと使えないようです。

OpenAIが法人営業で苦戦しているとの記事を目にしました。用途によるとは言え、買い手はAIの実力を正当に評価できているのか、はなはだ疑問です。他社サービスは文章生成では優れているのでしょうか。

インフルエンサーもインプットとアウトプットの量だけで評価しているふしがあり、質を網羅的にはほとんど見ていないですね。ちゃんと目利きができる方の出現を望みます。

プログラミング補助用途では、GPT-4の牙城は揺るぎないといったところです。

ただOpenAIには殿様商売的な姿勢が垣間見られ、アンチが多い感じがします。昨年11月、GPT-4 Turbo(gpt-4-1106-preview)のリリースについてはメールで案内がありましたが、今年1月のgpt-4-0125-previewリリースでは一切ありませんでした。この点については、かなり不満があります。

グチは置いといて、後発のGemini、Claudeはより大量な情報を処理し要約・創出するのが得意で、GPTの方はインプット側の足りない情報を補完するつまり行間を読む能力に長けている、という私なりの結論に至りました。

Claude 3 (Sonnet)※

※バージョン番号を取得する方法も含めて聞いているのだが、こちらの意図を読み取ることができない。
コード例を示している時点で指示者が中級者であることを察するべき。指示者がTextストラクチャを使えているのにその使い方を回答するのは流石にアウトです。単なる学習データ不足か。

GPT-4 : いつもながら素晴らしい回答
Gemini vs GPT-4の記事

[Swift] 70 Geminiアプリ製作検討 Google AI SDK for Swift

[Mac M2 Pro 12CPU, Sonoma 14.3.1, Xcode 15.2]

Googleの生成AIモデル Bardの後継モデル Geminiの実力を検証しました。今回はプログラミング補助としての評価です。

結果は散々でした。GPT-4 TurboどころかGPT-3.5の足元にも及ばない、という評価です。

料金が安いので期待していたのですが、とても残念です。あのGoogleですから、それなりのものを出してくると思っていました。

2024年2月時点では、GPT(OpenAI) >>> LLama(Meta)、圏外 Gemini(Google)といったところでしょうか。Geminiは比較以前の問題かと。

OpenAIの天下は当分続きそうです。Appleは完全に周回遅れですから、業務提携か買収しか道はないのでは。

gemini-proを使ったが、回答は0点。これでは厳しい。
GPT-4 Turbo:短いプロンプトでも意図を汲み取って完璧な回答

[Swift] 69 watchOS用ColorPicker作成

[Mac M2 Pro 12CPU, Ventura 13.6.1, iOS 17.2.1, Xcode 15.2]

久々の投稿です。

watchOS用に25色ColorPickerを作成しました。

import SwiftUI

struct ColorPickerAW: View {
    @Binding var selectedColor: Color
    var onColorSelected: (() -> Void)?

    // カラーコードをColorに変換する関数
    func colorFromHex(_ hex: String) -> Color {
        var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
        
        var rgb: UInt64 = 0
        
        Scanner(string: hexSanitized).scanHexInt64(&rgb)
        
        let red = Double((rgb & 0xFF0000) >> 16) / 255.0
        let green = Double((rgb & 0x00FF00) >> 8) / 255.0
        let blue = Double(rgb & 0x0000FF) / 255.0
        
        return Color(red: red, green: green, blue: blue)
    }
    
    // カラーコードの配列
    let colors: [String] = [
        "#FFFFFF", "#C0C0C0", "#808080", "#000000", "#000080",
        "#0000FF", "#00FFFF", "#40E0D0", "#008080", "#808000",
        "#008000", "#00FF00", "#F5F5DC", "#FFFF00", "#FFD700",
        "#FFA500", "#FF7F50", "#FF0000", "#800000", "#A52A2A",
        "#DDA0DD", "#E6E6FA", "#FFC0CB", "#FF00FF", "#800080"
    ]
    
    var body: some View {
        // グリッド表示
        LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 5), spacing: 10) {
            ForEach(colors, id: \.self) { colorCode in
                Rectangle()
                    .fill(colorFromHex(colorCode))
                    .frame(width: 25, height: 25) // 長方形のサイズ
                    .onTapGesture {
                        // タップされた色を選択
                        self.selectedColor = colorFromHex(colorCode)
                        // 親ビューに通知
                        self.onColorSelected?()
                    }
            }
        }
        .padding()
    }
}
VStack(){
    Toggle("Show_Era".localized, isOn: $eraName)
        .frame(width: 140, height: 80, alignment: .center)
        .foregroundColor(.blue)
        .font(.system(size: 24))
        .onChange(of: eraName) { newValue in
            UserDefaults.standard.set(newValue, forKey: eraNameKey)
            print("eraNameが\(newValue)に変更され、UserDefaultsに保存されました。")
        }
    HStack(spacing:2){
        Text("Watch\nRow1")
        Rectangle()
            .fill(colorWatch1)
            .frame(width: 30, height: 30)
            .opacity(1.0)
        Button("Set") {
            showingColorPicker = true
        }
    }
    
    Spacer()
}
.sheet(isPresented: $showingColorPicker) {
    ColorPickerAW(selectedColor: $selectedColor) {
        showingColorPicker = false
        colorWatch1 = selectedColor
    }
}

[Swift] 68 メモアプリ製作 その20 TestFlight移行時のトラブル CoreData / CloudKit Console

[Mac M2 Pro 12CPU, Ventura 13.6.1, iOS 17.2.1, Xcode 15.0]

・現況

2週間ぶりの投稿になります。

visionOS用アプリ・リリースの予行演習として、メモアプリのApp Store登録に没頭していました。

メモアプリ界隈はレッドオーシャンですから収益云々については全く期待していません。そもそもApp StoreアプリiOS版の貧弱な検索機能では私のアプリにたどり着けないでしょう。

・問題発覚

話を戻しますが、デバッグ版がほぼ完成したところで、リリース版を検証するためTestFlightに移行しました。移行当初はSANDBOXを使ったアプリ内課金やサブスクの検証しかしていなかったのですが、しばらくしてiCloudに配置しているCoreDataが使えなくなっていることに気が付きました。

Distribution証明書やProvisioning Profileの問題かと考え、何度も作成しなおしてはXcodeでArchiveをアップロードしていたところ、1日20回のアップロード制限に引っかかってしまいました。踏んだり蹴ったりとはこのことです。

仕方ないので捨てアプリ名でApp Store Connectに登録し直し*、色々試してみたものの解決には至らず1日超でギブアップしました。

・ようやく解決

しばらく頭を冷やしてから何となく検索語”cloudkit coredata release”でGoogle検索したところ、毎度お馴染みStackOverFlowの記事がヒットしました。

CloudKit ConsoleのCloudKit DatabaseでDeploy Schema Changesボタンを押し、次の画面でDeployボタンを押すと開発環境のCloudKitをリリース版で使えるようになる、とのことでした。AppleのCloudKit関連ドキュメントに説明があります。

CloudKitの扱いは慎重に行うべきということでしょう。なお1日後に解除とされていたArchiveのアップロード制限ですが、実際は13時間後解除でした。

結局、問題発覚から解決まで1日半掛かってしまいました。

・感想

開発者・配布者証明書を発行する際に使うキーチェーンアクセスや、Provisioning Profileなどを扱う”Certificates, Identifiers & Profiles”を散々いじり倒して大分使いこなせるようになってきましたし、CloudKitやCoreDataについて理解を深めることができたのは収穫でした。

もちろんChatGPTも駆使しましたが、回答の中でCloudKit Consoleの設定について触れることは1ミリもなかったです。プロンプトに問題がなかったか、時間があれば検証します。まあ雑な質問でもこれ位は答えて欲しいところではあります。

StackOverFlow記事

※ App Store Connectに登録したアプリは削除可能ですが、Bundle IdentifierはAppleサポートに削除してもらわないと再使用できないようです。App Store Connectには安易に登録しないのが無難でしょう。