ikeh1024のブログ

ZennやQiitaに書くにはちょっと小粒なネタをこちらに書いています

R.swiftの導入メモ

概要

image

image

エラーハンドリング

  • またUIテスト側でR.swiftを利用しようとするとエラーが出た。stringシンボルが見つからないということで、Localizable.stringsのTargetにをテスト側を追加すると解消した。
Value of type '_R' has no member 'string'

image

XcodeでInfo.plistの場所を変更する

  • Info.plistを下記のように別フォルダに移したい

image

  • 単に移してビルドするとplistが見つからないというエラーがでるので、下記のInfo.plist Fileのパスを更新すればOK

image

  • また下記の重複しているとのエラーが出たのでCopy Bundle ResourcesInfo.plistがある場合はそれを消すと良い
Prepare build
error: Multiple commands produce '/Users/ikeh/Library/Developer/Xcode/DerivedData/iOSEngineerCodeCheck-baojqjmcmqcbrrghemetsfapulot/Build/Products/Debug-iphonesimulator/iOSEngineerCodeCheck.app/Info.plist'
    note: Target 'iOSEngineerCodeCheck' (project 'iOSEngineerCodeCheck') has copy command from '/Users/ikeh/github-repository-search/iOSEngineerCodeCheck/Resources/Info.plist' to '/Users/ikeh/Library/Developer/Xcode/DerivedData/iOSEngineerCodeCheck-baojqjmcmqcbrrghemetsfapulot/Build/Products/Debug-iphonesimulator/iOSEngineerCodeCheck.app/Info.plist'
    note: Target 'iOSEngineerCodeCheck' (project 'iOSEngineerCodeCheck') has process command with output '/Users/ikeh/Library/Developer/Xcode/DerivedData/iOSEngineerCodeCheck-baojqjmcmqcbrrghemetsfapulot/Build/Products/Debug-iphonesimulator/iOSEngineerCodeCheck.app/Info.plist'


Multiple commands produce '/Users/ikeh/Library/Developer/Xcode/DerivedData/iOSEngineerCodeCheck-baojqjmcmqcbrrghemetsfapulot/Build/Products/Debug-iphonesimulator/iOSEngineerCodeCheck.app/Info.plist'

image

古いnibファイルをXcodeで読み込む際にエラーになった際の対処方法

  • かなり古いnibをXcodeで読み込もうとすると、下記のエラーが出て表示することすらできない問題にあたった。
Tried to create a document (class: IBXIBDocument) and got a document with a different, non-conforming fileType back instead.
ibtool LicenseInfo.nib --upgrade --write new/LicenseInfo.xib

SwiftPMの管理方法に、XcodeかPackage.swiftのどちらを採用するか

前提

  • SwiftPMの管理方法には2種類ある
    • Xcode上で管理する方法
    • Package.swiftで管理する方法
  • 特に理由がなければ基本的にXcode上で管理する方法を推奨

Package.swiftのユースケースと考慮する点

ユースケース

  • 基本的に外部公開用パッケージ・マルチモジュール構成のときに利用される
  • またDependabotやRenovateなどのライブラリのアップデートがあった際に、自動でPRをつくってくれるツールがあるが、この際にPackage.swiftを利用している必要がある
  • Xcodeの対応はIssuesにもあがっているが難しいみたい

https://team-blog.mitene.us/upgrade-swiftpm-library-renovate-71f7e8489775 Renovateのドキュメントに記載がある通り、 Package.swift 内のライブラリには対応していますが、Xcodeプロジェクトから入れたライブラリには対応していません。

考慮する点

  • Package.swiftはXcodeに付随している依存関係解決ツールのバージョンまで指定しているので、Xcodeのバージョンアップをしたりすると動かなくなることがある
  • 外部ライブラリにリソースを割くのが勿体ない

Xcode上で管理する方法」の場合のライブラリのアップデート方針

  • Renovateなどが利用できないので、ライブラリをアップデートするための方針を考える
  • 例えば以下の例がある

97:バージョンをいつ上げるのか

DjangoCeleryといった機能や影響が大きいフレームワークの場合、パッチバージョン(2.2.8→2.2.9) [1] の適用はこまめに行いましょう。

フレームワーク以外のライブラリ 1年に1回など、定期的にバージョンを更新していくのが良いでしょう。

開発用のツール flake8、mypy、pytest、toxといった開発中だけ使用するライブラリは、極端なことを言えばバージョンを上げる必要はありません。 半年以上継続する開発プロジェクトであれば、他のライブラリの更新時に合わせてバージョンアップする戦略が良いでしょう。

  • またはあるiOSエンジニアの方に聞いた方針は以下の通り
  • まずはライブラリのバージョンのルールを指定する
  • 破壊的な変更があり得るかどうかで分けるといい
    • Realmやアーキテクチャ系のライブラリなどの主要なライブラリは固定バージョン
    • その他のライブラリは常に最新のMajorバージョンになるように指定
  • 前者に関してライブラリの更新情報は随時キャッチアップしておき、影響がないかをチェックしたり、アップデートを検討する
    • キャッチアップする工夫として、GASで検知してSlackのチャンネルに投稿したり、iOS Osushiを利用すると良い

@AppStorage用のキーとreset関数を提供するSwift Macrosの作成

概要

  • Swift Macrosの習作として下記を作成するMacroを作成してみました。
    • @AppStorageに渡すKey
    • デフォルト値に戻すためのreset関数
  • https://github.com/pommdau/userdefaults-key-macro
    • (まだまだ理解が浅いので型推論の場合に未対応などいくつか問題は残っている)

モチベーション

  • 開発しているアプリで下記のようなコードがあって、泥臭い実装でなんだかなーと思って棚上げしていた箇所を、Swift Macrosで解決できるのではと考えた
  • 問題としては…
  • 文字列指定をさけるためのenum Propertyの宣言が面倒
  • 初期値を定義するためのdefaultParameterとかもあまり良くない書き方そう
extension RopeSettings {
        
    enum Property: String, CaseIterable {
        case needsShadow
        case ropeWidth
        case ropeOpacity
        case elasticity
        
        // e.g. "GeneralSettings-shapeSize"
        var key: String {
            "\(className)-\(rawValue)"
        }
        
        func defaultParameter<T>() -> T {                        
            switch self {
            case .needsShadow:
                return true as! T
            case .ropeWidth:
                return 6.0 as! T
            case .ropeOpacity:
                return 1.0 as! T
            case .elasticity:
                return Elasticity.elastic as! T
            }
        }
    }
    
    private static var className: String {
        String(describing: self)
    }
}

extension RopeSettings {
    static func resetUserDefaults() {
        Property.allCases.forEach { userDefaultsKey in
            UserDefaults.standard.removeObject(forKey: userDefaultsKey.key)
        }
    }    
}
import SwiftUI

class RopeSettings: ObservableObject {
    
    static let shared = RopeSettings()
    let settingsPaneType: SettingsPaneType = .rope
    
    // MARK: - User Settings
                                   
    @AppStorage(Property.needsShadow.key)
    var needsShadow: Bool = Property.needsShadow.defaultParameter()
    
    @AppStorage(Property.ropeWidth.key)
    var ropeWidth: Double = Property.ropeWidth.defaultParameter()
    
    @AppStorage(Property.ropeOpacity.key)
    var ropeOpacity: Double = Property.ropeOpacity.defaultParameter()
    
    @AppStorage(Property.elasticity.key)
    var elasticity: Elasticity = Property.elasticity.defaultParameter()
     
    init() {
//        Self.resetUserDefaults()
    }
}

PyAutoGUIのlocateCenterOnScreenは例外を投げるという話

概要

  • サンプルコードでlocateCenterOnScreenの返り値がNoneかどうかを判断するものがあるが、現状は例外エラーが投げられる。

  • plaza.rakuten.co.jp

  • www.fixes.pub

  • このようにエラーハンドリングをする
import pyautogui
# force use of ImageNotFoundException
pyautogui.useImageNotFoundException()
try:
    location= pyautogui.locateOnScreen('foo.png')
    print('image found')
except pyautogui.ImageNotFoundException:
    print('ImageNotFoundException: image not found')
  • これはちょっと認識違いかも?(pyscreezeではなくpuautogui側のImageNotFoundExceptionを使うべき)

teratail.com

pyautogui の内部で使われている pyscreeze が2度仕様変更しましたが、pyautogui が2度目の仕様変更に追従していないようです。

PC内のフォントファイルのURLを全取得する

  • 前提としてSandBoxをOFFにしておくこと
struct URLManager {
    
    /// 検索対象となるFontsディレクトリ
    static private var fontsDirectories: [URL] {
        guard
            let directoryInSystem = FileManager.default.urls(for: .libraryDirectory, in: .systemDomainMask).first?.appendingPathComponent("Fonts"),
            let directoryInGlobal = FileManager.default.urls(for: .libraryDirectory, in: .localDomainMask).first?.appendingPathComponent("Fonts"),
            let directoryInUser = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first?.appendingPathComponent("Fonts")
                
        else {
            return []
        }
        return [directoryInSystem, directoryInGlobal, directoryInUser]
    }
    
    /// 指定したディレクトリ内のフォントのfileURLを取得
    static private func loadFonts(in directory: URL) -> [URL] {
        guard let enumerator = FileManager.default.enumerator(
            at: directory,
            includingPropertiesForKeys: [.isRegularFileKey],
            options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
            return []
        }
        
        var fontURLs = [URL]()
        let extensions = ["otf", "ttf", "ttc"]
        for case let fileURL as URL in enumerator {
            if extensions.contains(fileURL.pathExtension) {
                fontURLs.append(fileURL)
            }
            fontURLs.append(fileURL)
        }
        return fontURLs
    }
    
    /// 検索対象となるFontsディレクトリ内の全フォントのURLを取得
    static func loadAllFontsInLocal() -> [URL] {
        var urls: [URL] = []
        fontsDirectories.forEach { directory in
            urls += loadFonts(in: directory)
        }
        urls.sort { $0.path() < $1.path() } // AtoZに並べ替え
        return urls
    }
    
}

SwiftのプロジェクトからC++の静的ライブラリを利用する

概要

  • Swiftのプロジェクトに対して、C++で書かれたライブラリのプロジェクトを追加して、アプリのビルド時にライブラリもビルドされるようにしたい

参考

手順

  • 下記の通りプロジェクトにライブラリのプロジェクトを追加

image

  • ライブラリのバイナリをリンクさせる

image

  • ライブラリのヘッダーファイルが含まれるディレクトリを指定

image

  • xxx-Bridging-Header.hを作成し利用するライブラリを記載する
#import "<library-name>.h"
  • 以上でSwiftから呼び出せるようになる(Swift側でのimportは不要)

Xcodeで特定のファイルのWarningを非表示にする

概要

In Xcode, how to suppress all warnings in specific source files? Select your target and show Build Phases. Then enter the name of the file in the search box, and you should see it listed in the Compile Sources phase. Double-click in the Compiler Flags column for that file and enter -w to turn off all warnings for that file.

image

macOSのMACOSX_DEPLOYMENT_TARGETの下限について

概要

  • 現行のXcode15.2.0ではMACOSX_DEPLOYMENT_TARGET(いわゆる最低対応OS)が10.13以上でしか選択できない
  • これはXcode14から10.13以上となったみたい
  • Xcode14とXcode13の差異として、macOS 12 SDKが含まれるかどうかがある

https://forums.developer.apple.com/forums/thread/714572 Xcode はプラットフォームごとに 1 つの SDK のみを同梱します。 Xcode 14 には古い macOS 12 SDK がまだ残っていますが、ベータ版には macOS 13 SDK が含まれていました。これは、昨年や一昨年と同様、新しい macOS バージョンがまだベータ版であり、Apple がベータ SDK をパブリック リリースとして出荷していないためです。 macOS 12 SDK では 10.9 へのデプロイが可能ですが、ベータ版で Xcode 14.1 に含まれる 13 SDK では 10.13 以降へのデプロイのみが可能です。

https://developer.apple.com/support/xcode/

image

  • SDKの場所は.app内の以下
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs

image

image

Xcodeと公証

  • ニュースとアップデート
  • macOSとしては公証を通すためにXcode14以上である必要がある
    • 公証を通すためにXcode13でビルドして、ローカルでnotarytoolコマンドでやればいけなくはないか…?

image

image

まとめ

  • MACOSX_DEPLOYMENT_TARGETを10.9にするには、下記の手順でいけそうな雰囲気(やりたくはない)
    • Xcodesを使ってXcode 13.xをダウンロード
    • Xcode.app内の実行バイナリを直接実行して起動
    • SchemeをReleaseにしてビルド(署名だけされている状態)
    • notarytoolコマンドで公証

NSStringのカテゴリ拡張: NFCへの正規化/安全なパーセントエンコーディング


  • NSString+Extension.h
#import <Foundation/Foundation.h>

@interface NSString (Extension)

- (NSString *)normalizeFormC;
- (NSString *)stringByRemovingPercentEncodingIfNeeded;

@end
  • NSString+Extension.m
#import "NSString+Extension.h"

@implementation NSString (Extension)

/**
 @brief  NFCに正規化する
 @return NFCに正規化されたNSString

 NSStringのデフォルトがNFCのためそちらに正規化する
 
 refs:
 [\[iOS\] 同じようにみえる文字が、\[NSString isEqualToString:\] で false になってしまう](https://qiita.com/plusadd/items/c2ec74eb2e19d068a421)
 [ファイルアップロードではNFC/NFD問題に気をつけろ!~MacファイルシステムにおけるUnicode正規化の闇~](https://zenn.dev/hacobell_dev/articles/68ccc92bffd6cc)
 */
- (NSString*)normalizeFormC {
    NSMutableString* _norstr = [NSMutableString stringWithString:self];
    CFStringNormalize((CFMutableStringRef)_norstr, kCFStringNormalizationFormC);
    
    return [NSString stringWithString:_norstr];
}

/**
 @brief 必要であればstringByRemovingPercentEncodingを行う
 
 refs:
 [stringByRemovingPercentEncoding](https://developer.apple.com/documentation/foundation/nsstring/1409569-stringbyremovingpercentencoding)
 >このメソッドは、パーセントでエンコードされていることがわかっている文字列に対してのみ呼び出す必要があります。パーセントエンコードされていない文字列に対してこのメ<200b><200b>ソッドを呼び出すと、
 >パーセント文字がパーセントエンコードされたシーケンスの始まりであると誤って解釈される可能性があります。
 */
- (NSString *)stringByRemovingPercentEncodingIfNeeded {
    NSCharacterSet *allowedCharacterSet = [NSCharacterSet URLQueryAllowedCharacterSet];
    NSCharacterSet *disallowedCharacterSet = [allowedCharacterSet invertedSet];
    NSRange range = [self rangeOfCharacterFromSet:disallowedCharacterSet];
    if (range.location != NSNotFound) {
        // パーセントエンコーディングされている場合
        return self.stringByRemovingPercentEncoding;
    } else {
        return self;
    }
}

@end

pyenvでPythonのインストール

  • pythonのバージョン確認とインストール

Poetryをサクッと使い始めてみる

# インストールされているバージョンを確認
pyenv versions 

# インストール可能なバージョンを表示
pyenv install --list

# 指定したバージョンをインストール
pyenv install <python-version>

# グローバルなデフォルトを指定したバージョンに変更
pyenv global <python-version>
export LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib $LDFLAGS"
export CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include $CPPFLAGS"
export PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@1.1/lib/pkgconfig"

pyenvのアップデート

  • 利用できるPythonのバージョンはpyenvのバージョンに依存する?のか、pyenvの更新が必要だった
brew upgrade pyenv

python 3.12.2のインストール時のエラー

ginstall: cannot stat 'Modules/_ssl.cpython-312-darwin.so': No such file or directory

github.com

macOSアプリの各サイズのアイコンを自動作成するツール

  • いろいろ試してみてこれが一番良かった。出力されたフォルダをAssetsへドラッグ・アンド・ドロップでOK。
  • ちなみに一つの画像を用意すればOK、な仕組みは現状macOSには無いようです。残念。
  • Single Sizeがない

MacでTwitter用に動画を変換するときのメモ

  • Twitter用に.movをアップロードしようとしたらエラー
  • 下記でmp4に変換できる。

convert .mov to .m4v with mac terminal

brew install ffmpeg
ffmpeg -i input.mov -acodec copy -vcodec copy output.m4v
  • ただしサイズ(1920*1024だったっけ?)も考慮が必要で、QuickTimePlayerで720pで書き出すなどしてエラーが解消した。
  • WindowsのようなTwitter動画変換くんみたいなツールを作って楽にしたい。)
  • フレームレートを下げるのに一応使えた変換サイト(ただしオンラインサービスなので色々注意)