如何将Objective-C中的模块移植到Swift?

时间:2014-06-09 17:10:13

标签: objective-c swift

在尝试了几个小的Swift程序后,我决定下一步是将Objective-C程序中的单个模块移植到Swift中,看看需要哪些步骤。我有很多问题,所以我想我会在这里发布我的流程和结果,以防其他人发现它有用。

我还创建了一个表来帮助我记住不同的转换。很遗憾,StackOverflow不支持表格,因此我将这些转化发布为Github gist here

虽然Apple无疑会提供一个Xcode Refactor来从Objective-C转换为Swift,但手动转换它是熟悉这两种语言之间差异的好方法。你熟悉的语言中有很多“肌肉记忆”,这是熟悉新语法的好方法。正如苹果公司所承诺的那样,这些语言共享了许多共同的想法,它主要是一个机械过程(而不是移植,比如C ++,甚至传统的C)。

请注意,此过程不使用Swift令人兴奋的新功能,它只能直接获取代码。我应该提一下,转移到Swift将限制任何向后兼容iOS 7或OS X 10.9。我还遇到了一些问题(下面有解决方法),我确信这只是项目的第一个测试版发布状态,因此未来版本可能不需要。

我选择了iPhoneCoreDataRecipes并选择了一个不依赖于其他许多模块的模块:IngredientDetailViewController。如果您想跟随,请查看下面的“答案”。

希望这是有用的。

1 个答案:

答案 0 :(得分:2)

0)下载项目here的副本并在Xcode版本6中打开Recipes.xcodeproj

1)选择File>New File…>iOS Source>Swift File> IngredientDetailViewController(文件夹:类,组:配方视图控制器)

2)回复是“你想配置一个Objective-C桥接头吗?”

3)将Recipes_Prefix.pch下面的前三行和IngredientDetailViewController.m中的后三行复制到Recipes-Bridging-Header.h。如果您执行其他文件,显然不会重复行,并删除您已转换为Swift的所有文件。鉴于它们已经在swift文件中导入,我还没有找到任何记录Cocoa系列需求的地方,但是......

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "Recipe.h"
#import "Ingredient.h"
#import "EditingTableViewCell.h"

4)将IngredientDetailViewController.h文件和IngredientDetailViewController.m文件中的文本复制/粘贴到IngredientDetailViewController.swift

5)从项目中删除IngredientDetailViewController.h.m个文件。

6)从#import "IngredientDetailViewController.h"#import "Recipes-Swift.h"执行全局查找和替换(在这种情况下只进行一次转换,对于其他文件,请不要在目标中复制此行) -C模块。)

7)检查项目&gt;目标&gt;食谱&gt;构建设置Runpath Search Paths。如果它显示$(inherited),请删除此行,否则您在发布时会收到错误&#34;未找到图像&#34;

8)将IngredientDetailViewController.swift中的Objective-C语法转换为Swift。请参阅所需的GitHub Gist mentioned above替换,或转换后的版本。

9)您可能需要更新IB链接。在IngredientDetailViewController上执行查找&gt;在文件中查找,然后在Interface Builder中选择一个。在右侧列中打开Identity Inspector。在类别字段中选择IngredientDetailViewController,输入xxx或其他内容和标签。

10)构建并运行。请注意,进入配方后,您必须点击编辑,然后点击成分的信息按钮以激活IngredientDetailViewController

12)祝贺您构建第一个混合的Swift / Objective-C程序!

这是我对这个特定模块的切入:

``

class IngredientDetailViewController: UITableViewController {

    var recipe: Recipe!
    var ingredient: Ingredient! {
    willSet {

        if let newIngredient = newValue {
            self.ingredientStr = newIngredient.name
            self.amountStr = newIngredient.amount
        } else {
            self.ingredientStr = ""
            self.amountStr = ""
        }
    }
    }
    init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
        super.init(nibName:nibNameOrNil, bundle: nibBundleOrNil?)
    }
    init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }
    init(style: UITableViewStyle) {
        super.init(style: style)
    }

    // MARK: table's data source
    var ingredientStr: String?
    var amountStr: String?

    // view tags for each UITextField
    let kIngredientFieldTag =    1
    let kAmountFieldTag  = 2



    override func viewDidLoad () {

        super.viewDidLoad()

        self.title = "Ingredient"

        self.tableView.allowsSelection = false
        self.tableView.allowsSelectionDuringEditing = false
    }


    override  func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {

        let IngredientsCellIdentifier = "IngredientsCell"
        let cell = tableView.dequeueReusableCellWithIdentifier(IngredientsCellIdentifier, forIndexPath: indexPath ) as EditingTableViewCell
        if (indexPath.row == 0) {
            // cell ingredient name

            cell.label.text = "Ingredient"
            cell.textField.text = self.ingredientStr
            cell.textField.placeholder = "Name"
            cell.textField.tag = kIngredientFieldTag
        }
        else if (indexPath.row == 1) {

            // cell ingredient amount
            cell.label.text = "Amount"
            cell.textField.text = self.amountStr
            cell.textField.placeholder = "Amount"
            cell.textField.tag = kAmountFieldTag
        }

        return cell
    }



    @IBAction func  save (sender: AnyObject!) {

        if let context = self.recipe.managedObjectContext  {
            if (!self.ingredient) {
                self.ingredient = NSEntityDescription.insertNewObjectForEntityForName("Ingredient",
                    inManagedObjectContext:context) as Ingredient
                self.recipe.addIngredientsObject(self.ingredient)
                self.ingredient.displayOrder = self.recipe.ingredients.count
            }

            // update the ingredient from the values in the text fields
            let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow:0, inSection:0)) as EditingTableViewCell
            self.ingredient.name = cell.textField.text


            // save the managed object context
            var error: NSError? = nil
            if !context.save( &error)  {
                /*
                Replace this implementation with code to handle the error appropriately.

                abort() causes the application to generate a crash log and terminate.
                You should not use this function in a shipping application, although it may be
                useful during development. If it is not possible to recover from the error, display
                an alert panel that instructs the user to quit the application by pressing the Home button.
                */
                println("Unresolved error \(error), \(error!.userInfo)")
                abort()
            }

        }
        // if there isn't an ingredient object, create and configure one

        self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
    }

    @IBAction func cancel(sender: AnyObject!) {

        self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
    }

    func textFieldDidEndEditing(textField:UITextField) {

        // editing has ended in one of our text fields, assign it's text to the right
        // ivar based on the view tag
        //
        switch (textField.tag)
            {
        case kIngredientFieldTag:
            self.ingredientStr = textField.text

        case kAmountFieldTag:
            self.amountStr = textField.text
        default:
            break
        }
    }
}