| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

「Swift Sample」の版間の差分

提供: MyMemoWiki
ナビゲーションに移動 検索に移動
 
(同じ利用者による、間の10版が非表示)
40行目: 40行目:
 
}
 
}
 
</pre>
 
</pre>
*書き出し処理
+
*読み込み、コレクションに追加、書き出し処理
 
<pre>
 
<pre>
 +
    func addHostListDocument(host: WoL.Host) {
 
         let docPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
 
         let docPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
 
         let filePath = URL(fileURLWithPath: "hosts.json", relativeTo:docPath)
 
         let filePath = URL(fileURLWithPath: "hosts.json", relativeTo:docPath)
 +
       
 +
        var hostList: [WoL.Host] = []
 +
       
 +
        let decoder = JSONDecoder()
 +
        do {
 +
            let rawData = try Data(contentsOf: filePath)
 +
            print(rawData)
 +
            let tmpHostList = try decoder.decode([WoL.Host].self, from: rawData)
 +
            for tmpHost in tmpHostList {
 +
                hostList.append(tmpHost)
 +
            }
 +
        } catch {
 +
            print(error)
 +
        }
 +
       
 +
        hostList.append(host)
 
         let encoder = JSONEncoder()
 
         let encoder = JSONEncoder()
 
         do {
 
         do {
             let line = try encoder.encode(host)
+
             let line = try encoder.encode(hostList)
 
             try line.write(to: filePath)
 
             try line.write(to: filePath)
 
         } catch {
 
         } catch {
 
             print(error)
 
             print(error)
 
         }
 
         }
 +
    }
 
</pre>
 
</pre>
 
[[File:swift_json_encode.png|300px]]
 
[[File:swift_json_encode.png|300px]]
 +
=====書式を指定=====
 +
<pre>
 +
        let encoder = JSONEncoder()
 +
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
 +
</pre>
 +
=====Stringに変換=====
 +
<pre>
 +
      let json = try encoder.encode(hosts.hosts)
 +
      let jsonString = String(data: json, encoding: .utf8)!
 +
      print(jsonString)
 +
</pre>
 +
=====画面とオブジェクトを共有する=====
 +
*struct -> class
 +
*ObservableObject を適用
 +
*@Published
 +
*上記を適用すると、Codable に適合させるには、明示的に処理を記述する必要がある
 +
<pre>
 +
class Host : Codable, ObservableObject {
 +
    @Published var host: String = ""
 +
    @Published var ip: String = ""
 +
    @Published var macaddr: String = ""
 +
    @Published var comment: String = ""
 +
 +
    init(host: String, ip: String, macaddr: String, comment: String) {
 +
        self.host = host
 +
        self.ip = ip
 +
        self.macaddr = macaddr
 +
        self.comment = comment
 +
    }
 +
    init() {}
 +
 +
    /// 変換対象プロパティ
 +
    enum CodingKeys: CodingKey {
 +
        case host
 +
        case ip
 +
        case macaddr
 +
        case comment
 +
    }
 +
 +
    required init(from decoder: Decoder) throws {
 +
        let container = try decoder.container(keyedBy: CodingKeys.self)
 +
        host = try container.decode(String.self, forKey: .host)
 +
        ip = try container.decode(String.self, forKey: .ip)
 +
        macaddr = try container.decode(String.self, forKey: .macaddr)
 +
        comment = try container.decode(String.self, forKey: .comment)
 +
    }
 +
 +
    func encode(to encoder: Encoder) throws {
 +
        var container = encoder.container(keyedBy: CodingKeys.self)
 +
        try container.encode(host, forKey: .host)
 +
        try container.encode(ip, forKey: .ip)
 +
        try container.encode(macaddr, forKey: .macaddr)
 +
        try container.encode(comment, forKey: .comment)
 +
    }
 +
</pre>
 +
 +
===ディレクトリ判定===
 +
----
 +
<pre>
 +
private static let fm = FileManager.default
 +
static func isDirectory(path: String) -> Bool {
 +
    var isDir = ObjCBool(false)
 +
    fm.fileExists(atPath: path, isDirectory: &isDir)
 +
    return isDir.boolValue
 +
}
 +
</pre>
  
 
==UI==
 
==UI==
66行目: 150行目:
 
DispatchQueue.main.sync {
 
DispatchQueue.main.sync {
 
     observableobj.content = text
 
     observableobj.content = text
 +
}
 +
</pre>
 +
===ディレクトリを選択===
 +
----
 +
<pre>
 +
static func chooseDir() -> String {
 +
 +
let dialog = NSOpenPanel();
 +
 +
dialog.title                  = "Choose a root directory"
 +
dialog.showsResizeIndicator    = true
 +
dialog.showsHiddenFiles        = false
 +
dialog.allowsMultipleSelection = false
 +
dialog.canChooseDirectories    = true
 +
dialog.canChooseFiles          = false
 +
 +
if (dialog.runModal() ==  NSApplication.ModalResponse.OK) {
 +
let result = dialog.url
 +
 +
if (result != nil) {
 +
let path: String = result!.path
 +
return path
 +
}
 +
 +
}
 +
return "";
 
}
 
}
 
</pre>
 
</pre>
71行目: 181行目:
 
==処理==
 
==処理==
 
===サブプロセスを起動===
 
===サブプロセスを起動===
 +
----
 
*プロセスを起動し、arp -a を呼び出し、出力
 
*プロセスを起動し、arp -a を呼び出し、出力
 
<pre>
 
<pre>
95行目: 206行目:
 
     }
 
     }
 
</pre>
 
</pre>
 +
 +
===非同期処理===
 +
----
 +
<pre>
 +
let c = {
 +
(ip:String) -> String in
 +
let hostName = getHostName(ip: ip) // 時間がかかる処理
 +
return hostName
 +
}
 +
 +
let que = DispatchQueue.global(qos: .default)
 +
for host in hosts.hosts {
 +
print("CALL-\(host.ip)")
 +
que.async {  // 非同期処理
 +
let hostname = c(host.ip)  // 時間がかかる処理
 +
DispatchQueue.main.async { // 画面に反映
 +
host.host = hostname
 +
}
 +
}
 +
}
 +
</pre>
 +
 
==[[正規表現]]==
 
==[[正規表現]]==
 
===arp -a の結果からIPアドレスを抜きだす===
 
===arp -a の結果からIPアドレスを抜きだす===
127行目: 260行目:
 
     }
 
     }
 
</pre>
 
</pre>
 +
===digを経由してBonjourでIPアドレスをホスト名に解決===
 +
*コマンドをswiftで呼び出し dig +short -x 192.168.0.45 @224.0.0.251 -p 5353
 +
<pre>
 +
        let outputPipe = Pipe()
 +
        let _ = shell(outputPipe,
 +
                      path:"/usr/bin/dig",
 +
                      args: "+short",
 +
                      "-x", ip,
 +
                      "@224.0.0.251", "-p","5353") // Bonjour IPアドレス
 +
        let theTaskData = outputPipe.fileHandleForReading.readDataToEndOfFile()
 +
        let stringResult = String(data: theTaskData, encoding: .utf8)
 +
</pre>
 +
 
===名前を付与してキャプチャ===
 
===名前を付与してキャプチャ===
 
----
 
----

2022年3月19日 (土) 01:48時点における最新版

| Swift | SwiftUI |Xcode | Mac |

Swift Sample

Network

データ取得


  1. let url = URL(string: "https://www.typea.info/blog/")!
  2. let task = URLSession.shared.dataTask(with: url) { data, response, error in
  3. if let error = error {
  4. print("\(error)\n")
  5. return
  6. }
  7. guard let data = data, let response = response as? HTTPURLResponse else {
  8. print("no data or no response.")
  9. return
  10. }
  11. if response.statusCode == 200 {
  12. if let text = String(bytes: data, encoding: .utf8) {
  13. print(text)
  14. }
  15. }
  16. }
  17. task.resume()

MacOSでエラーの場合

Error Domain=NSURLErrorDomain Code=-1003

Swift macos net error.png

File

JSONにシリアライズしてドキュメントディレクトリへ書き出し

  • データ(Codableを適用する)
  1. struct Host : Codable {
  2. var host: String = ""
  3. var ip: String = ""
  4. var macaddr: String = ""
  5. }
  • 読み込み、コレクションに追加、書き出し処理
  1. func addHostListDocument(host: WoL.Host) {
  2. let docPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
  3. let filePath = URL(fileURLWithPath: "hosts.json", relativeTo:docPath)
  4. var hostList: [WoL.Host] = []
  5. let decoder = JSONDecoder()
  6. do {
  7. let rawData = try Data(contentsOf: filePath)
  8. print(rawData)
  9. let tmpHostList = try decoder.decode([WoL.Host].self, from: rawData)
  10. for tmpHost in tmpHostList {
  11. hostList.append(tmpHost)
  12. }
  13. } catch {
  14. print(error)
  15. }
  16. hostList.append(host)
  17. let encoder = JSONEncoder()
  18. do {
  19. let line = try encoder.encode(hostList)
  20. try line.write(to: filePath)
  21. } catch {
  22. print(error)
  23. }
  24. }

Swift json encode.png

書式を指定
  1. let encoder = JSONEncoder()
  2. encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
Stringに変換
  1. let json = try encoder.encode(hosts.hosts)
  2. let jsonString = String(data: json, encoding: .utf8)!
  3. print(jsonString)
画面とオブジェクトを共有する
  • struct -> class
  • ObservableObject を適用
  • @Published
  • 上記を適用すると、Codable に適合させるには、明示的に処理を記述する必要がある
  1. class Host : Codable, ObservableObject {
  2. @Published var host: String = ""
  3. @Published var ip: String = ""
  4. @Published var macaddr: String = ""
  5. @Published var comment: String = ""
  6.  
  7. init(host: String, ip: String, macaddr: String, comment: String) {
  8. self.host = host
  9. self.ip = ip
  10. self.macaddr = macaddr
  11. self.comment = comment
  12. }
  13. init() {}
  14.  
  15. /// 変換対象プロパティ
  16. enum CodingKeys: CodingKey {
  17. case host
  18. case ip
  19. case macaddr
  20. case comment
  21. }
  22.  
  23. required init(from decoder: Decoder) throws {
  24. let container = try decoder.container(keyedBy: CodingKeys.self)
  25. host = try container.decode(String.self, forKey: .host)
  26. ip = try container.decode(String.self, forKey: .ip)
  27. macaddr = try container.decode(String.self, forKey: .macaddr)
  28. comment = try container.decode(String.self, forKey: .comment)
  29. }
  30.  
  31. func encode(to encoder: Encoder) throws {
  32. var container = encoder.container(keyedBy: CodingKeys.self)
  33. try container.encode(host, forKey: .host)
  34. try container.encode(ip, forKey: .ip)
  35. try container.encode(macaddr, forKey: .macaddr)
  36. try container.encode(comment, forKey: .comment)
  37. }

ディレクトリ判定


  1. private static let fm = FileManager.default
  2. static func isDirectory(path: String) -> Bool {
  3. var isDir = ObjCBool(false)
  4. fm.fileExists(atPath: path, isDirectory: &isDir)
  5. return isDir.boolValue
  6. }

UI

バックグラウンドからUIを操作する


  • observableobj が、ObservableObject の派生クラス
  • contentフィールドに、@Published アノテーション
  • Viewで、@ObservedObjectを付与しインスタンスを生成
  • 上記で、バックグラウンドから、observableobj.contentを操作すると、UIはメインスレッドから触るように怒られる。

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

  • DispatchQueue.main.syncで囲む
  1. DispatchQueue.main.sync {
  2. observableobj.content = text
  3. }

ディレクトリを選択


  1. static func chooseDir() -> String {
  2. let dialog = NSOpenPanel();
  3.  
  4. dialog.title = "Choose a root directory"
  5. dialog.showsResizeIndicator = true
  6. dialog.showsHiddenFiles = false
  7. dialog.allowsMultipleSelection = false
  8. dialog.canChooseDirectories = true
  9. dialog.canChooseFiles = false
  10.  
  11. if (dialog.runModal() == NSApplication.ModalResponse.OK) {
  12. let result = dialog.url
  13.  
  14. if (result != nil) {
  15. let path: String = result!.path
  16. return path
  17. }
  18. }
  19. return "";
  20. }

処理

サブプロセスを起動


  • プロセスを起動し、arp -a を呼び出し、出力
  1. func arp() {
  2. let outputPipe = Pipe()
  3.  
  4. func shell(path:String ,args: String...) -> Int32 {
  5. let task = Process()
  6. task.launchPath = path
  7. task.arguments = args
  8. task.standardOutput = outputPipe
  9. task.standardError = outputPipe
  10. task.launch()
  11. task.waitUntilExit()
  12. return task.terminationStatus
  13. }
  14. let _ = shell(path:"/usr/sbin/arp",args: "-a")
  15. let theTaskData = outputPipe.fileHandleForReading.readDataToEndOfFile()
  16. let stringResult = String(data: theTaskData, encoding: .utf8)
  17. print(stringResult!)
  18. }

非同期処理


  1. let c = {
  2. (ip:String) -> String in
  3. let hostName = getHostName(ip: ip) // 時間がかかる処理
  4. return hostName
  5. }
  6.  
  7. let que = DispatchQueue.global(qos: .default)
  8. for host in hosts.hosts {
  9. print("CALL-\(host.ip)")
  10. que.async { // 非同期処理
  11. let hostname = c(host.ip) // 時間がかかる処理
  12. DispatchQueue.main.async { // 画面に反映
  13. host.host = hostname
  14. }
  15. }
  16. }

正規表現

arp -a の結果からIPアドレスを抜きだす


  1. func parseArp(arpResult: String?) {
  2. if let input = arpResult {
  3. do {
  4. let pattern = #"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"#
  5. let regex = try NSRegularExpression(pattern: pattern, options:[])
  6. for subinput in input.split(separator: "\r\n") {
  7. let line = String(subinput)
  8. let maches = regex.matches(in: line, options: [], range: _NSRange(0..<line.count))
  9. print(line)
  10. for mach in maches {
  11. for i in 0 ..< mach.numberOfRanges {
  12. let start = line.index(line.startIndex, offsetBy: mach.range(at: i).location)
  13. let end = line.index(start, offsetBy: mach.range(at: i).length)
  14. let text = String(line[start..<end])
  15. print(text)
  16. }
  17. }
  18. }
  19. } catch {
  20. print("RegEx fail.")
  21. }
  22. } else {
  23. print("arp -a result : error")
  24. }

digを経由してBonjourでIPアドレスをホスト名に解決

  • コマンドをswiftで呼び出し dig +short -x 192.168.0.45 @224.0.0.251 -p 5353
  1. let outputPipe = Pipe()
  2. let _ = shell(outputPipe,
  3. path:"/usr/bin/dig",
  4. args: "+short",
  5. "-x", ip,
  6. "@224.0.0.251", "-p","5353") // Bonjour IPアドレス
  7. let theTaskData = outputPipe.fileHandleForReading.readDataToEndOfFile()
  8. let stringResult = String(data: theTaskData, encoding: .utf8)

名前を付与してキャプチャ


  1. func parseArp(arpResult: String?) {
  2. if let input = arpResult {
  3. do {
  4. let pattern = #".*?(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*(?<mac>[0-9a-z]{1,2}:[0-9a-z]{1,2}:[0-9a-z]{1,2}:[0-9a-z]{1,2}:[0-9a-z]{1,2}:[0-9a-z]{1,2})"#
  5. let regex = try NSRegularExpression(pattern: pattern, options:[])
  6. for subinput in input.split(separator: "\r\n") {
  7. let line = String(subinput)
  8. let matches = regex.matches(in: line, options: [], range: _NSRange(0..<line.count))
  9. print(line)
  10. for match in matches {
  11. for name in ["ip", "mac"] {
  12. let matchRange = match.range(withName: name)
  13. if let substrRanget = Range(matchRange, in:line) {
  14. let ip = String(line[substrRanget])
  15. print("\(name):\(ip)")
  16. }
  17. }
  18. }
  19. }
  20. } catch {
  21. print("RegEx fail.")
  22. }
  23. } else {
  24. print("arp -a result : error")
  25. }
  26. }