ikeh1024のブログ

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

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動画変換くんみたいなツールを作って楽にしたい。)

Peripheryの導入とHomebrew・Sandboxのエラー対応

概要

エラー対応

periphery: command not found

  • 下記のエラーが発生。
/Users/ikeh/Library/Developer/Xcode/DerivedData/periphery-sample-ebunibmbisdaeaaxlfjwgzvlgizm/Build/Intermediates.noindex/periphery-sample.build/Debug-iphonesimulator/Periphery.build/Script-DA09C6AC2AD536D400FED882.sh: line 5: periphery: command not found
Command PhaseScriptExecution failed with a nonzero exit code
  • /opt/homebrew/binのPATHが通っていないのが原因

M1 環境で Homebrew 導入コマンドを Xcode から叩けない場合の対処法 M1 (Apple Silicon) 環境の Homebrew については、デフォルトのインストール先が /opt/homebrew/bin (+ /opt/homebrew/sbin) へと変更されました。

export PATH=$PATH:/opt/homebrew/bin
periphery scan --project periphery-sample.xcodeproj --schemes periphery-sample --targets periphery-sample --format xcode

The project doesn't contain a .pbxproj file at path:

  • 続いて下記のエラーが発生。
(XCodeProjError) The project doesn't contain a .pbxproj file at path: /Users/ikeh/Downloads/Instance/periphery-sample/periphery-sample.xcodeproj

  • 以上で正常に動くようになりました。

参考

UserDefaultsのplistの場所とリセット

Screenshots

image

image

Code

struct ContentView: View {
    
    // MARK: - Properties
        
    var library: URL? {
        FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
    }
    
    var preferences: URL? {
        library?.appendingPathComponent("Preferences")
    }
    
    var userDefaultsPlist: URL? {
        guard let preferences,
              let bundleIdentifier = Bundle.main.bundleIdentifier else {
            return nil
        }
        return preferences.appendingPathComponent(bundleIdentifier).appendingPathExtension("plist")
    }
    
    @AppStorage("text") var text = "sample string"
        
    // MARK: - View
    
    var body: some View {
        VStack(alignment: .leading) {
            
            TextField("Enter your text", text: $text)
            
            GroupBox("UserDefaults value") {
                Text(text)
            }
            
            Button("Reset UserDefaults") {
                if let appDomain = Bundle.main.bundleIdentifier {
                    UserDefaults.standard.removePersistentDomain(forName: appDomain)
                }
            }
            .frame(maxWidth: .infinity, alignment: .trailing)
            
            Divider()
            GroupBox("UserDefaults path") {
                Text("\(userDefaultsPlist?.path ?? "")")
            }
            Button("Show in Finder") {
                if let userDefaultsPlist,
                   FileManager.default.fileExists(atPath: userDefaultsPlist.path) {
                    NSWorkspace.shared.selectFile(userDefaultsPlist.path,
                                                  inFileViewerRootedAtPath: "")
                }
            }
            .frame(maxWidth: .infinity, alignment: .trailing)
        }
        .textSelection(.enabled)
        .padding()
    }
}

References

アプリのフレームワークを確認する方法のメモ

C言語のデータ型とSwiftのデータ型の対応表

  • GitHub Copilotで書き出したときのメモ
  • (間違いあったらご指摘ください)
C言語のデータ型 Swiftのデータ型
char Int8
unsigned char UInt8
short Int16
unsigned short UInt16
int Int32
unsigned int UInt32
long Int
unsigned long UInt
long long Int64
unsigned long long UInt64
float Float
double Double
long double CGFloat

SwiftでMacのシリアル番号とハードウェアUUIDを取得

概要

  • Swiftで下記に記載されているシリアル番号とハードウェアUUIDを取得する

image

実装

func loadIOPlatformExpertDevice(ioService: String, propertyKey: String) -> String? {
    let service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching(ioService))
    if service <= 0 {
        return nil
    }
    
    guard let _property = IORegistryEntryCreateCFProperty(service, propertyKey as CFString, kCFAllocatorDefault, 0),
          let property = _property.takeUnretainedValue() as? String
    else {
        return nil
    }
    IOObjectRelease(service)
    
    return property
}
  • 呼び出し例
let keys = [
   kIOPlatformSerialNumberKey,
   kIOPlatformUUIDKey,
]
keys.forEach { key in
    if let value = loadIOPlatformExpertDevice(ioService: "IOPlatformExpertDevice", propertyKey: key) {
        print(value)
    }
}

参考

UDIDからUUIDへ

UDIDの使用が禁止される理由 広告業者(モバイル広告提供者)がこのUDIDを使って、特定の利用者(UDID)がどのような広告にアクセスし、どんなアプリを使ったり買ったりしているかという個人情報を取得できます。そして、利用者がこれを防ぐ手段がないのでプライバシーの侵害に繋がると米国議会から非難されたからです。

Macアドレスはだめ

How can I query the hardware UUID of a Mac programmatically from a command line?

ioreg -d2 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $(NF-1)}'

他の情報

macOSアプリからPythonを呼び出そうと試みたときのメモ

結論

  • 今のところ、ピュアなPythonは組み込んで使えそう
  • 3rd partyのライブラリを含める方法は分からずじまい

参考

set -e
echo "Signing contents of $CODESIGNING_FOLDER_PATH/Contents/Resources/python-stdlib/lib-dynload as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)"
cd "$CODESIGNING_FOLDER_PATH/Contents/Resources/python-stdlib/lib-dynload"
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" --options runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der *.so

Could you elaborate on how you managed to integrate a 3rd party library?


Sure. In my case I wanted to add librosa library, and basically I added a script that will download it dependencies to a folder i have control over python3 -m pip install --target="${CODESIGNING_FOLDER_PATH}/Contents/Resources/" librosa then, in my Python setup I added this path to my PYTHONPATH let resources = bundle.bundlePath.appending("/Contents/Resources/") setenv("PYTHONPATH", "(stdLibPath):(libDynloadPath):(resources)", 1) I have an example on github https://github.com/brenovaladao/PythonSample

GeometryReaderをbackgroundで呼び出してビューのサイズを取得するのはバッドプラクティス

import SwiftUI

struct ContentView: View {
    
    @State private var realViewSize = CGSize(width: 200, height: 200)
    @State private var viewSize: CGSize = .zero
    
    var body: some View {
        VStack {
            HStack {
                Button("+") {
                    realViewSize.width += 10
                    realViewSize.height += 10
                }
                Button("-") {
                    realViewSize.width -= 10
                    realViewSize.height -= 10
                }
            }
            Text("\(realViewSize.debugDescription)")
            Text("\(viewSize.debugDescription)")
        }
        .frame(width: realViewSize.width, height: realViewSize.height)
        .background(.blue.opacity(0.3))
        .background() {
            viewSizeGettger()
        }
        .padding()
    }
    
    @ViewBuilder
    private func viewSizeGettger() -> some View {
        GeometryReader { geometry in
            Color.clear
                .onAppear {
                    viewSize = geometry.size
                }
        }
    }
}

#Preview {
    ContentView()
}

image

SwiftUIのToggleでget/setを利用してBinding<Bool>を渡す

image

import SwiftUI

class Manager: ObservableObject {
    @Published var lastUpdatedAt: Date = Date()
    @Published var isToggleOn = false
}

struct ContentView: View {
    
    @StateObject private var manager = Manager()
    
    var isToggleOn: Binding<Bool> {
        Binding<Bool>(
            get: { manager.isToggleOn },
            set: { newValue in
                manager.isToggleOn = newValue
                print("ここに処理を挟めるよ!")
                manager.lastUpdatedAt = .now
            }
        )
    }
    
    var body: some View {
        VStack(alignment: .leading) {
            Toggle("Toggle Button", isOn: isToggleOn)
                .toggleStyle(.switch)
            Text("Last Update: \(manager.lastUpdatedAt)")
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}