Xcode 旧バージョンをダウンロードとインストール

客先でのシステム要件Xcodeが古い場合が多々あります。そういった場合Xcodeの古いバージョンをインストールして使うことになります。Xcodeは複数の旧バージョンをインストールすることも可能なので客先側と同じ開発環境に合わせることができます。まずは、旧バージョンのXcodeダウンロード方法です。

https://developer.apple.com/download/

上記URLのApple Developer Downloadサイトを開き右上にあるMoreをクリックします。

下のようなダウンロード一覧画面が表示されます。

左側に入力フィールドがあるのでそこでXcodeのバージョンを絞り込むことができます。

一覧の中から旧バージョンのXcodeを選択し(Xcode 11.3.1)、Xcode 11.3.1.xip(7.3GByte)をダウンロードします。

ダウンロードしたあとFinder上でダブルクリックするとXcodeアイコンが出てきます。

ローカルのmacOSにインストールされている最新のXcodeも使いつつ旧バージョンのXcodeも使いたいので、ダウンロードフォルダにあるXcodeアイコンのファイル名を変更します。私の場合旧バージョンのXcodeは「Xcode11.3.1」という名前に変更しました。

先程リネームしたXcode11.3.1をアプリケーションフォルダにコピーします。

アプリケーションフォルダにコピーできたあと、すぐには起動せずターミナルを使ってXcodeのDirectory Pathを変更します。

ターミナルを開き、「/usr/bin/xcode-select」を使ってXcodeのPathを旧バージョンのXcodeにPath変更します。

sudo xcode-select --switch /Applications/Xcode11.3.1.app

正常にPathが変更されているか確認します。

  ~ xcode-select --print-path
/Applications/Xcode11.3.1.app/Contents/Developer

Xcode11.3.1.appになっていることが確認できたら Xcode11.3.1(旧バージョン)を起動します。

最新のXcodeにPathを戻す場合は、同じようにターミナルから下記コマンドを入力します。

sudo xcode-select --switch /Applications/Xcode.app

SwiftUI Int to Binding<String>【数値を文字列バインディング】

SwiftUIは、View側のBindingを使うことで変数などの変更を即表示させることが可能です。

TextFieldの引数textはBinding<String>型なので@Stateなどの文字列変数をBindingできます。

struct ContentView: View {
    @State var str1: String = "文字列"
    var body: some View {
        VStack{
            TextField("Edit1",text: $str1) 
        }
    }
}

しかし、@Stateを数値にしている場合TextFieldのtextにバインディング設定できません。

Cannot convert value of type 'Binding<Int>' to expected argument type 'Binding<String>'

このような場合extension Binding でBinding<Int>からBinding<String>変換します。

extension Binding where Value == Int {
    func IntToStrDef(_ def: Int) -> Binding<String> {
        return Binding<String>(get: {
            return String(self.wrappedValue)
        }) { value in
            self.wrappedValue = Int(value) ?? def
        }
    }
}

extensionの設定が完了したら@State varのところのStringIntに変更してみましょう。

上記のようにIntToStrDefが追加されています。

struct ContentView: View {
    @State var i1: Int = 0
    var body: some View {
        VStack{
            TextField("Edit1",text: $i1.IntToStrDef(0)) 
        }
    }
}

上記のようにIntBindingできました。

https://forums.swift.org/t/promoting-binding-value-to-binding-value/31055

SwiftUI ObservableObjectのobjectWillChange

前回、ObservableObjectを継承したクラスを使用したコードを紹介しました。そのクラスを使用しobjectWillChangeイベントを試してみます。

class TObserver:ObservableObject  {
    @Published var num: Int
    init(_ i1: Int) {
        self.num = i1
    }
}

前回と同じく ObservableObjectを継承したTObserverという名称のクラスを作成しました。
クラス内にはnumという数値型の変数を用意しています。

View側で@ObservedObjectを指定した TObserverを用意し VStackのonAppear時にobjectWillChangeイベントを設定します。

onAppearはビューが表示されるときに実行されます。

下記は実装したコードです。

struct ContentView: View {
    @ObservedObject var obj1 = TObserver(100)
    @State var func1: Any?
    var body: some View {
        VStack{
            Button("Button1", action: {
                self.obj1.num = 200
            })
        }.onAppear(perform: {
            self.func1 = self.obj1.objectWillChange.sink(receiveValue: self.changeAction1)
        })
    }
    
    func changeAction1(){
        print("Change before = " + String(obj1.num))
        print("「obj1」の内容変更を知らせる")
    }
}

onAppear内の処理は、@Stateで指定したfunc1: AnyにobjectWillChange.sink()を代入しています。これは、obj1TObserver」の内容変更があった場合にchangeAction1を実行するように指定しています。func1を用意しない場合changeAction1は実行されませんでした。“Button1”のactionには、obj1.num200を入れて変更させています。この“Button1″をクリックするとchangeAction1が実行されるようになります。obj1の変更される前の状態がobjectWillChangeに入ります。