macOS Catalina でSubversionの利用

仕事上どうしてもmacOSSubversionを使わないといけなかったので、langui.netのSnailSVNをインストールしてみました。

SnailSVNTortoiseSVNのようなApache Subversion(SVN)クライアントで、Finder拡張ができるようです。

Apple Storeからインストールができます。

SnailSVNを、インストールし起動すると下のようなセットアップダイアログが表示されます。

ここで、「Specify the SVN binaries you would like to use.」SVNバイナリバージョン指定と書いているのですが、SVNを使ったことが無いのでそのまま[Next]ボタンでつぎの画面に行きました。(あとで変更できるようです)

次にPath設定画面が出ました。これもよくわからないのでそのまま[Next]。

Pathを指定しない場合Defaultの値が入るようです。[Continue]ボタンをクリックしつぎへ。

最後に、「You need to enable “SnailSVN Extension” in “System Preferences » Extensions” to make the context menu appear.」[システム環境設定|拡張機能]で[SnailSVN拡張機能]を有効にする必要があります。と書いています。[Finish]ボタンをクリックします。

It seems that “SnailSVN Extension” has not been enabled yet, would you like to continue anyway?「SnailSVN拡張機能」有効なっていません。続行しますか?と聞かれるので、ここも[Continue]ボタンをクリックします。

[SnailSVN Preferences]画面が開きます。いろんなオプションが設定できるようです。一番したの[Open System Preferences]ボタンをクリックします。

[システム環境設定|拡張機能]が開きますので画面左側の[Finder拡張機能]を選択し[SnailSVN Extension]にチェックが入っていないのでチェックを入れます

[SnailSVN Preferences]の上部にメニューボタンがあったので、[SVN Setting]を選択し[SVN Path]の「Bundled」をチェック「1.11.x」を選びます。そのあと[Working Copies]メニューボタンをクリックしました。

Working Copies]には何も入っていませんでした。

インストールは恐らくここまでです。このあとサーバーへ接続するための設定を行います。

これから、客先様から指定頂いたSVNサーバーへ接続してみます。

SnailSVNのメニューから[FileSVN Checkout… ⌘O]を選択します。

URL or Repository]に客先から指定されたURLをコピペし、[Checkout Directory]には作業用Directoryを指定します。[OK]ボタンを押します。

[Username]と[Password]を入力し[OK]ボタンをクリックします。ユーザー情報が失敗している場合下記のようなメッセージが出ます。

svn: E215004: Unable to connect to a repository at URL ‘https://****’
svn: E215004: No more credentials or we tried too many times.
Authentication failed

SVNサーバーのRepository接続が成功した場合、下のようにダウンロードが開始されます。

SVNサーバーのRepositoryからダウンロードが完了すれば[Working Copies]にはRepositoryが追加されていることが確認できます。

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に入ります。