blundin
blundin

Reputation: 1643

Why does the Swift compiler throw: "Could not find an overload for '<' that accepts the supplied arguments"

I am using FCModel in some new Swift code to persist my model data. In my schema management/migration code I get a compile time error when I perform a logical comparison on an Int. I cannot tell if there is a syntax or type error here, or if I have found a compiler bug.

Is this a valid logical operation? Is there a mistake I missed?

Error: AppDelegate.swift:53:30: Could not find an overload for '<' that accepts the supplied arguments

Relevant code:

var schemaVersion: Int = (settings["schemaVersion"] as String).toInt()!

FCModel.openDatabaseAtPath(databasePath, withSchemaBuilder: { database, schemaVersion in
    database.crashOnErrors = true
    database.traceExecution = true
    database.beginTransaction()

// 'failedAt' closure removed for brevity

if schemaVersion < 1 { // ERROR HAPPENS HERE
    if !database.executeUpdate("SQL STATEMENT HERE", withArgumentsInArray: nil)
    { failedAt(1) }

    schemaVersion = 1
}

Update openDatabaseAtPath() is defined as:

func openDatabaseAtPath(path: String!, withSchemaBuilder schemaBuilder: ((FMDatabase!, CMutablePointer<CInt>) -> Void)!)

Update #2 With Sulthan's help below I think I've narrowed down where the issue is, but I have no idea why. Again, I think this may be a bug in Swift.

Updated code:

var schemaVersion: Int = 0
let database: FMDatabase = FMDatabase(path: databasePath)

FCModel.openDatabaseAtPath(databasePath, withSchemaBuilder: { (database, schemaVersion: CMutablePointer<CInt>) in
    var x:Int = Int(schemaVersion.withUnsafePointer( { (unsafeSchemaVersion: UnsafePointer<CInt>) -> CInt in
        return unsafeSchemaVersion.memory
    }))
    ...
})

When I debug and use the REPL in Xcode something interesting happens. When execution is in the withSchemaBuilder block database is not nil, and matches the object created above. schemaVersion is not a recognized identifier:

error: use of unresolved identifier 'schemaVersion'
schemaVersion

and:

$R0: FMDatabase! = {
  Some = {
    NSObject = {
      isa = FMDatabase
    }
    _db =
    _databasePath = @"/Users/brian/Library/Developer/CoreSimulator/Devices/25115FDE-0DA8-4F2B-B4E5-5A0600720772/data/Containers/Data/Application/579A64D7-6C6B-4690-B503-2CA5B4229D55/Documents/userData.sqlite"
    _logsErrors = YES
    _crashOnErrors = NO
    _traceExecution = NO
    _checkedOut = NO
    _shouldCacheStatements = NO
    _isExecutingStatement = NO
    _inTransaction = NO
    _maxBusyRetryTimeInterval = 2
    _startBusyRetryTime = 0
    _cachedStatements = nil
    _openResultSets = 0 objects
    _openFunctions = nil
    _dateFormat = nil
  }
}

Any thoughts?

Upvotes: 1

Views: 1689

Answers (1)

Sulthan
Sulthan

Reputation: 130102

The declaration

var schemaVersion: Int = (settings["schemaVersion"] as String).toInt()!

doesn't have anything to do with your error since you are redefining schemaVersion locally here:

... withSchemaBuilder: { database, schemaVersion in 

The method is defined as

+ (void)openDatabaseAtPath:(NSString *)path
         withSchemaBuilder:(void (^)(FMDatabase *db, int *schemaVersion))schemaBuilder;

so I assume the type of schemaVersion in the block will be something like CMutablePointer <CInt>

Logically, you can't compare that to an Int (1) directly. If it's really an CMutablePointer<Int>, you should use something like:

schemaVersion.withUnsafePointer { unsafeSchemaVersion in
    if unsafeSchemaVersion.memory < 1

    ...

    unsafeSchemaVersion.memory = 1
}

That's equivalent to the following in Obj-C:

 if (*schemaVersion < 1) {
     ...
     *schemaVersion = 1;
 }

If you want to reference the variable you have defined outside the closure, you should rename it or rename the closure parameter.

Upvotes: 4

Related Questions